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), psRT1Info(nullptr), psRT2Info(nullptr),
183 0 : 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 : nRT1RecOffset = 0;
204 :
205 0 : if (poDS->GetVersion() >= TIGER_2000_Redistricting)
206 : {
207 0 : psRT3Info = &rt3_2000_Redistricting_info;
208 : }
209 : else
210 : {
211 0 : psRT3Info = &rt3_info;
212 : }
213 :
214 : /* -------------------------------------------------------------------- */
215 : /* Fields from type 1 record. */
216 : /* -------------------------------------------------------------------- */
217 :
218 0 : AddFieldDefns(psRT1Info, poFeatureDefn);
219 :
220 : /* -------------------------------------------------------------------- */
221 : /* Fields from type 3 record. Eventually we should verify that */
222 : /* a .RT3 file is available before adding these fields. */
223 : /* -------------------------------------------------------------------- */
224 0 : if (bUsingRT3)
225 : {
226 0 : AddFieldDefns(psRT3Info, poFeatureDefn);
227 : }
228 0 : }
229 :
230 : /************************************************************************/
231 : /* ~TigerCompleteChain() */
232 : /************************************************************************/
233 :
234 0 : TigerCompleteChain::~TigerCompleteChain()
235 :
236 : {
237 0 : CPLFree(panShapeRecordId);
238 :
239 0 : if (fpRT3 != nullptr)
240 0 : VSIFCloseL(fpRT3);
241 :
242 0 : if (fpShape != nullptr)
243 0 : VSIFCloseL(fpShape);
244 0 : }
245 :
246 : /************************************************************************/
247 : /* SetModule() */
248 : /************************************************************************/
249 :
250 0 : bool TigerCompleteChain::SetModule(const char *pszModuleIn)
251 :
252 : {
253 0 : if (!OpenFile(pszModuleIn, "1"))
254 0 : return false;
255 :
256 0 : EstablishFeatureCount();
257 :
258 : /* -------------------------------------------------------------------- */
259 : /* Is this a copyright record inserted at the beginning of the */
260 : /* RT1 file by the folks at GDT? If so, setup to ignore the */
261 : /* first record. */
262 : /* -------------------------------------------------------------------- */
263 0 : nRT1RecOffset = 0;
264 0 : if (pszModuleIn)
265 : {
266 : char achHeader[10];
267 :
268 0 : VSIFSeekL(fpPrimary, 0, SEEK_SET);
269 0 : VSIFReadL(achHeader, sizeof(achHeader), 1, fpPrimary);
270 :
271 0 : if (STARTS_WITH_CI(achHeader, "Copyright"))
272 : {
273 0 : nRT1RecOffset = 1;
274 0 : nFeatures--;
275 : }
276 : }
277 :
278 : /* -------------------------------------------------------------------- */
279 : /* Open the RT3 file */
280 : /* -------------------------------------------------------------------- */
281 0 : if (bUsingRT3)
282 : {
283 0 : if (fpRT3 != nullptr)
284 : {
285 0 : VSIFCloseL(fpRT3);
286 0 : fpRT3 = nullptr;
287 : }
288 :
289 0 : if (pszModuleIn)
290 : {
291 0 : char *pszFilename = poDS->BuildFilename(pszModuleIn, "3");
292 :
293 0 : fpRT3 = VSIFOpenL(pszFilename, "rb");
294 :
295 0 : CPLFree(pszFilename);
296 : }
297 : }
298 :
299 : /* -------------------------------------------------------------------- */
300 : /* Close the shape point file, if open and free the list of */
301 : /* record ids. */
302 : /* -------------------------------------------------------------------- */
303 0 : if (fpShape != nullptr)
304 : {
305 0 : VSIFCloseL(fpShape);
306 0 : fpShape = nullptr;
307 : }
308 :
309 0 : CPLFree(panShapeRecordId);
310 0 : panShapeRecordId = nullptr;
311 :
312 : /* -------------------------------------------------------------------- */
313 : /* Try to open the RT2 file corresponding to this RT1 file. */
314 : /* -------------------------------------------------------------------- */
315 0 : if (pszModuleIn != nullptr)
316 : {
317 0 : char *pszFilename = poDS->BuildFilename(pszModuleIn, "2");
318 :
319 0 : fpShape = VSIFOpenL(pszFilename, "rb");
320 :
321 0 : if (fpShape == nullptr)
322 : {
323 0 : if (nRT1RecOffset == 0)
324 0 : CPLError(CE_Warning, CPLE_OpenFailed,
325 : "Failed to open %s, intermediate shape arcs will not "
326 : "be available.\n",
327 : pszFilename);
328 : }
329 : else
330 0 : panShapeRecordId =
331 0 : (int *)CPLCalloc(sizeof(int), (size_t)GetFeatureCount());
332 :
333 0 : CPLFree(pszFilename);
334 : }
335 :
336 0 : return true;
337 : }
338 :
339 : /************************************************************************/
340 : /* GetFeature() */
341 : /************************************************************************/
342 :
343 0 : OGRFeature *TigerCompleteChain::GetFeature(int nRecordId)
344 :
345 : {
346 : char achRecord[OGR_TIGER_RECBUF_LEN];
347 :
348 0 : if (nRecordId < 0 || nRecordId >= nFeatures)
349 : {
350 0 : CPLError(CE_Failure, CPLE_FileIO,
351 : "Request for out-of-range feature %d of %s1", nRecordId,
352 : pszModule);
353 0 : return nullptr;
354 : }
355 :
356 : /* -------------------------------------------------------------------- */
357 : /* Read the raw record data from the file. */
358 : /* -------------------------------------------------------------------- */
359 0 : if (fpPrimary == nullptr)
360 0 : return nullptr;
361 :
362 : {
363 0 : const auto nOffset =
364 0 : static_cast<uint64_t>(nRecordId + nRT1RecOffset) * nRecordLength;
365 0 : if (VSIFSeekL(fpPrimary, nOffset, SEEK_SET) != 0)
366 : {
367 0 : CPLError(CE_Failure, CPLE_FileIO,
368 : "Failed to seek to %" PRIu64 " of %s1", nOffset,
369 : pszModule);
370 0 : return nullptr;
371 : }
372 : }
373 :
374 : // Overflow cannot happen since psRTInfo->nRecordLength is unsigned
375 : // char and sizeof(achRecord) == OGR_TIGER_RECBUF_LEN > 255
376 0 : if (VSIFReadL(achRecord, psRT1Info->nRecordLength, 1, fpPrimary) != 1)
377 : {
378 0 : CPLError(CE_Failure, CPLE_FileIO,
379 : "Failed to read %d bytes of record %d of %s1 at offset %d",
380 0 : psRT1Info->nRecordLength, nRecordId, pszModule,
381 0 : (nRecordId + nRT1RecOffset) * nRecordLength);
382 0 : return nullptr;
383 : }
384 :
385 : /* -------------------------------------------------------------------- */
386 : /* Set fields. */
387 : /* -------------------------------------------------------------------- */
388 :
389 0 : auto poFeature = std::make_unique<OGRFeature>(poFeatureDefn);
390 :
391 0 : SetFields(psRT1Info, poFeature.get(), achRecord);
392 :
393 : /* -------------------------------------------------------------------- */
394 : /* Read RT3 record, and apply fields. */
395 : /* -------------------------------------------------------------------- */
396 :
397 0 : if (fpRT3 != nullptr)
398 : {
399 : char achRT3Rec[OGR_TIGER_RECBUF_LEN];
400 0 : int nRT3RecLen =
401 0 : psRT3Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength;
402 :
403 0 : const auto nOffset = static_cast<uint64_t>(nRecordId) * nRT3RecLen;
404 0 : if (VSIFSeekL(fpRT3, nOffset, SEEK_SET) != 0)
405 : {
406 0 : CPLError(CE_Failure, CPLE_FileIO,
407 : "Failed to seek to %" PRIu64 " of %s3", nOffset,
408 : pszModule);
409 0 : return nullptr;
410 : }
411 :
412 : // Overflow cannot happen since psRTInfo->nRecordLength is unsigned
413 : // char and sizeof(achRecord) == OGR_TIGER_RECBUF_LEN > 255
414 0 : if (VSIFReadL(achRT3Rec, psRT3Info->nRecordLength, 1, fpRT3) != 1)
415 : {
416 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to read record %d of %s3",
417 : nRecordId, pszModule);
418 0 : return nullptr;
419 : }
420 :
421 0 : SetFields(psRT3Info, poFeature.get(), achRT3Rec);
422 : }
423 :
424 : /* -------------------------------------------------------------------- */
425 : /* Set geometry */
426 : /* -------------------------------------------------------------------- */
427 0 : auto poLine = std::make_unique<OGRLineString>();
428 :
429 0 : poLine->setPoint(0, atoi(GetField(achRecord, 191, 200)) / 1000000.0,
430 0 : atoi(GetField(achRecord, 201, 209)) / 1000000.0);
431 :
432 0 : if (!AddShapePoints(poFeature->GetFieldAsInteger("TLID"), nRecordId,
433 : poLine.get(), 0))
434 : {
435 0 : return nullptr;
436 : }
437 :
438 0 : poLine->addPoint(atoi(GetField(achRecord, 210, 219)) / 1000000.0,
439 0 : atoi(GetField(achRecord, 220, 228)) / 1000000.0);
440 :
441 0 : poFeature->SetGeometryDirectly(poLine.release());
442 :
443 0 : return poFeature.release();
444 : }
445 :
446 : /************************************************************************/
447 : /* AddShapePoints() */
448 : /* */
449 : /* Record zero or more shape records associated with this line */
450 : /* and add the points to the passed line geometry. */
451 : /************************************************************************/
452 :
453 0 : bool TigerCompleteChain::AddShapePoints(int nTLID, int nRecordId,
454 : OGRLineString *poLine,
455 : CPL_UNUSED int nSeqNum)
456 : {
457 0 : int nShapeRecId = GetShapeRecordId(nRecordId, nTLID);
458 :
459 : // -2 means an error occurred.
460 0 : if (nShapeRecId == -2)
461 0 : return false;
462 :
463 : // -1 means there are no extra shape vertices, but things worked fine.
464 0 : if (nShapeRecId == -1)
465 0 : return true;
466 :
467 : /* -------------------------------------------------------------------- */
468 : /* Read all the sequential records with the same TLID. */
469 : /* -------------------------------------------------------------------- */
470 : char achShapeRec[OGR_TIGER_RECBUF_LEN];
471 0 : const int nShapeRecLen =
472 0 : psRT2Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength;
473 :
474 0 : for (; true; nShapeRecId++)
475 : {
476 0 : int nBytesRead = 0;
477 :
478 0 : const auto nOffset =
479 0 : static_cast<uint64_t>(nShapeRecId - 1) * nShapeRecLen;
480 0 : if (VSIFSeekL(fpShape, nOffset, SEEK_SET) != 0)
481 : {
482 0 : CPLError(CE_Failure, CPLE_FileIO,
483 : "Failed to seek to %" PRIu64 " of %s2", nOffset,
484 : pszModule);
485 0 : return false;
486 : }
487 :
488 0 : nBytesRead = static_cast<int>(
489 0 : VSIFReadL(achShapeRec, 1, psRT2Info->nRecordLength, fpShape));
490 :
491 : /*
492 : ** Handle case where the last record in the file is full. We will
493 : ** try to read another record but not find it. We require that we
494 : ** have found at least one shape record for this case though.
495 : */
496 0 : if (nBytesRead <= 0 && VSIFEofL(fpShape) && poLine->getNumPoints() > 0)
497 0 : break;
498 :
499 0 : if (nBytesRead != psRT2Info->nRecordLength)
500 : {
501 0 : CPLError(CE_Failure, CPLE_FileIO,
502 : "Failed to read %d bytes of record %d of %s2 at offset %d",
503 0 : psRT2Info->nRecordLength, nShapeRecId, pszModule,
504 0 : (nShapeRecId - 1) * nShapeRecLen);
505 0 : return false;
506 : }
507 :
508 0 : if (atoi(GetField(achShapeRec, 6, 15)) != nTLID)
509 0 : break;
510 :
511 : /* --------------------------------------------------------------------
512 : */
513 : /* Translate the locations into OGRLineString vertices. */
514 : /* --------------------------------------------------------------------
515 : */
516 0 : int iVertex = 0; // Used after for.
517 :
518 0 : for (; iVertex < 10; iVertex++)
519 : {
520 0 : const int iStart = 19 + 19 * iVertex;
521 0 : const int nX = atoi(GetField(achShapeRec, iStart, iStart + 9));
522 : const int nY =
523 0 : atoi(GetField(achShapeRec, iStart + 10, iStart + 18));
524 :
525 0 : if (nX == 0 && nY == 0)
526 0 : break;
527 :
528 0 : poLine->addPoint(nX / 1000000.0, nY / 1000000.0);
529 : }
530 :
531 : /* --------------------------------------------------------------------
532 : */
533 : /* Don't get another record if this one was incomplete. */
534 : /* --------------------------------------------------------------------
535 : */
536 0 : if (iVertex < 10)
537 0 : break;
538 0 : }
539 :
540 0 : return true;
541 : }
542 :
543 : /************************************************************************/
544 : /* GetShapeRecordId() */
545 : /* */
546 : /* Get the record id of the first record of shape points for */
547 : /* the provided TLID (complete chain). */
548 : /************************************************************************/
549 :
550 0 : int TigerCompleteChain::GetShapeRecordId(int nChainId, int nTLID)
551 :
552 : {
553 0 : CPLAssert(nChainId >= 0 && nChainId < GetFeatureCount());
554 :
555 0 : if (fpShape == nullptr || panShapeRecordId == nullptr)
556 0 : return -1;
557 :
558 : /* -------------------------------------------------------------------- */
559 : /* Do we already have the answer? */
560 : /* -------------------------------------------------------------------- */
561 0 : if (panShapeRecordId[nChainId] != 0)
562 0 : return panShapeRecordId[nChainId];
563 :
564 : /* -------------------------------------------------------------------- */
565 : /* If we don't already have this value, then search from the */
566 : /* previous known record. */
567 : /* -------------------------------------------------------------------- */
568 : int iTestChain, nWorkingRecId;
569 :
570 0 : for (iTestChain = nChainId - 1;
571 0 : iTestChain >= 0 && panShapeRecordId[iTestChain] <= 0; iTestChain--)
572 : {
573 : }
574 :
575 0 : if (iTestChain < 0)
576 : {
577 0 : iTestChain = -1;
578 0 : nWorkingRecId = 1;
579 : }
580 : else
581 : {
582 0 : nWorkingRecId = panShapeRecordId[iTestChain] + 1;
583 : }
584 :
585 : /* -------------------------------------------------------------------- */
586 : /* If we have non existent records following (-1's) we can */
587 : /* narrow our search a bit. */
588 : /* -------------------------------------------------------------------- */
589 0 : while (panShapeRecordId[iTestChain + 1] == -1)
590 : {
591 0 : iTestChain++;
592 : }
593 :
594 : /* -------------------------------------------------------------------- */
595 : /* Read records up to the maximum distance that is possibly */
596 : /* required, looking for our target TLID. */
597 : /* -------------------------------------------------------------------- */
598 0 : int nMaxChainToRead = nChainId - iTestChain;
599 0 : int nChainsRead = 0;
600 : char achShapeRec[OGR_TIGER_RECBUF_LEN];
601 0 : int nShapeRecLen =
602 0 : psRT2Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength;
603 :
604 0 : if (nShapeRecLen <= 0)
605 : {
606 0 : return -2;
607 : }
608 :
609 0 : while (nChainsRead < nMaxChainToRead)
610 : {
611 0 : const auto nOffset =
612 0 : static_cast<uint64_t>(nWorkingRecId - 1) * nShapeRecLen;
613 0 : if (VSIFSeekL(fpShape, nOffset, SEEK_SET) != 0)
614 : {
615 0 : CPLError(CE_Failure, CPLE_FileIO,
616 : "Failed to seek to %" PRIu64 " of %s2", nOffset,
617 : pszModule);
618 0 : return -2;
619 : }
620 :
621 0 : if (VSIFReadL(achShapeRec, psRT2Info->nRecordLength, 1, fpShape) != 1)
622 : {
623 0 : if (!VSIFEofL(fpShape))
624 : {
625 0 : CPLError(CE_Failure, CPLE_FileIO,
626 : "Failed to read record %d of %s2", nWorkingRecId - 1,
627 : pszModule);
628 0 : return -2;
629 : }
630 : else
631 0 : return -1;
632 : }
633 :
634 0 : if (atoi(GetField(achShapeRec, 6, 15)) == nTLID)
635 : {
636 0 : panShapeRecordId[nChainId] = nWorkingRecId;
637 :
638 0 : return nWorkingRecId;
639 : }
640 :
641 0 : if (atoi(GetField(achShapeRec, 16, 18)) == 1)
642 : {
643 0 : nChainsRead++;
644 : }
645 :
646 0 : nWorkingRecId++;
647 : }
648 :
649 0 : panShapeRecordId[nChainId] = -1;
650 :
651 0 : return -1;
652 : }
|