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