Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: TIGER/Line Translator
4 : * Purpose: Implements TigerBaseFile class, providing common services to all
5 : * the tiger file readers.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
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 : #include "cpl_error.h"
34 : #include "cpl_string.h"
35 :
36 : #include <cinttypes>
37 :
38 : /************************************************************************/
39 : /* TigerFileBase() */
40 : /************************************************************************/
41 :
42 0 : TigerFileBase::TigerFileBase(const TigerRecordInfo *psRTInfoIn,
43 0 : const char *m_pszFileCodeIn)
44 : : poDS(nullptr), pszModule(nullptr), pszShortModule(nullptr),
45 : fpPrimary(nullptr), poFeatureDefn(nullptr), nFeatures(0),
46 : nRecordLength(0), nVersionCode(0), nVersion(TIGER_Unknown),
47 0 : psRTInfo(psRTInfoIn), m_pszFileCode(m_pszFileCodeIn)
48 : {
49 0 : }
50 :
51 : /************************************************************************/
52 : /* ~TigerFileBase() */
53 : /************************************************************************/
54 :
55 0 : TigerFileBase::~TigerFileBase()
56 :
57 : {
58 0 : CPLFree(pszModule);
59 0 : CPLFree(pszShortModule);
60 :
61 0 : if (poFeatureDefn != nullptr)
62 : {
63 0 : poFeatureDefn->Release();
64 0 : poFeatureDefn = nullptr;
65 : }
66 :
67 0 : if (fpPrimary != nullptr)
68 : {
69 0 : VSIFCloseL(fpPrimary);
70 0 : fpPrimary = nullptr;
71 : }
72 0 : }
73 :
74 : /************************************************************************/
75 : /* OpenFile() */
76 : /************************************************************************/
77 :
78 0 : int TigerFileBase::OpenFile(const char *pszModuleToOpen,
79 : const char *pszExtension)
80 :
81 : {
82 :
83 0 : CPLFree(pszModule);
84 0 : pszModule = nullptr;
85 0 : CPLFree(pszShortModule);
86 0 : pszShortModule = nullptr;
87 :
88 0 : if (fpPrimary != nullptr)
89 : {
90 0 : VSIFCloseL(fpPrimary);
91 0 : fpPrimary = nullptr;
92 : }
93 :
94 0 : if (pszModuleToOpen == nullptr)
95 0 : return TRUE;
96 :
97 0 : char *pszFilename = poDS->BuildFilename(pszModuleToOpen, pszExtension);
98 :
99 0 : fpPrimary = VSIFOpenL(pszFilename, "rb");
100 :
101 0 : CPLFree(pszFilename);
102 :
103 0 : if (fpPrimary == nullptr)
104 0 : return FALSE;
105 :
106 0 : pszModule = CPLStrdup(pszModuleToOpen);
107 0 : pszShortModule = CPLStrdup(pszModuleToOpen);
108 0 : for (int i = 0; pszShortModule[i] != '\0'; i++)
109 : {
110 0 : if (pszShortModule[i] == '.')
111 0 : pszShortModule[i] = '\0';
112 : }
113 :
114 0 : SetupVersion();
115 :
116 0 : return TRUE;
117 : }
118 :
119 : /************************************************************************/
120 : /* SetupVersion() */
121 : /************************************************************************/
122 :
123 0 : void TigerFileBase::SetupVersion()
124 :
125 : {
126 : char aszRecordHead[6];
127 :
128 0 : VSIFSeekL(fpPrimary, 0, SEEK_SET);
129 0 : VSIFReadL(aszRecordHead, 1, 5, fpPrimary);
130 0 : aszRecordHead[5] = '\0';
131 0 : nVersionCode = atoi(aszRecordHead + 1);
132 0 : VSIFSeekL(fpPrimary, 0, SEEK_SET);
133 :
134 0 : nVersion = TigerClassifyVersion(nVersionCode);
135 0 : }
136 :
137 : /************************************************************************/
138 : /* EstablishRecordLength() */
139 : /************************************************************************/
140 :
141 0 : int TigerFileBase::EstablishRecordLength(VSILFILE *fp)
142 :
143 : {
144 0 : if (fp == nullptr || VSIFSeekL(fp, 0, SEEK_SET) != 0)
145 0 : return -1;
146 :
147 : /* -------------------------------------------------------------------- */
148 : /* Read through to the end of line. */
149 : /* -------------------------------------------------------------------- */
150 0 : int nRecLen = 0;
151 0 : char chCurrent = '\0';
152 0 : while (VSIFReadL(&chCurrent, 1, 1, fp) == 1 && chCurrent != 10 &&
153 0 : chCurrent != 13)
154 : {
155 0 : nRecLen++;
156 : }
157 :
158 : /* -------------------------------------------------------------------- */
159 : /* Is the file zero length? */
160 : /* -------------------------------------------------------------------- */
161 0 : if (nRecLen == 0)
162 : {
163 0 : return -1;
164 : }
165 :
166 0 : nRecLen++; /* for the 10 or 13 we encountered */
167 :
168 : /* -------------------------------------------------------------------- */
169 : /* Read through line terminator characters. We are trying to */
170 : /* handle cases of CR, CR/LF and LF/CR gracefully. */
171 : /* -------------------------------------------------------------------- */
172 0 : while (VSIFReadL(&chCurrent, 1, 1, fp) == 1 &&
173 0 : (chCurrent == 10 || chCurrent == 13))
174 : {
175 0 : nRecLen++;
176 : }
177 :
178 0 : VSIFSeekL(fp, 0, SEEK_SET);
179 :
180 0 : return nRecLen;
181 : }
182 :
183 : /************************************************************************/
184 : /* EstablishFeatureCount() */
185 : /************************************************************************/
186 :
187 0 : void TigerFileBase::EstablishFeatureCount()
188 :
189 : {
190 0 : if (fpPrimary == nullptr)
191 0 : return;
192 :
193 0 : nRecordLength = EstablishRecordLength(fpPrimary);
194 :
195 0 : if (nRecordLength == -1)
196 : {
197 0 : nRecordLength = 1;
198 0 : nFeatures = 0;
199 0 : return;
200 : }
201 :
202 : /* -------------------------------------------------------------------- */
203 : /* Now we think we know the fixed record length for the file */
204 : /* (including line terminators). Get the total file size, and */
205 : /* divide by this length to get the presumed number of records. */
206 : /* -------------------------------------------------------------------- */
207 :
208 0 : VSIFSeekL(fpPrimary, 0, SEEK_END);
209 0 : const vsi_l_offset nFileSize = VSIFTellL(fpPrimary);
210 :
211 0 : if ((nFileSize % (vsi_l_offset)nRecordLength) != 0)
212 : {
213 0 : CPLError(CE_Warning, CPLE_FileIO,
214 : "TigerFileBase::EstablishFeatureCount(): "
215 : "File length %d doesn't divide by record length %d.\n",
216 0 : (int)nFileSize, (int)nRecordLength);
217 : }
218 :
219 0 : if (nFileSize / (vsi_l_offset)nRecordLength > (vsi_l_offset)INT_MAX)
220 0 : nFeatures = INT_MAX;
221 : else
222 0 : nFeatures = static_cast<int>(nFileSize / (vsi_l_offset)nRecordLength);
223 : }
224 :
225 : /************************************************************************/
226 : /* GetField() */
227 : /************************************************************************/
228 :
229 0 : const char *TigerFileBase::GetField(const char *pachRawDataRecord,
230 : int nStartChar, int nEndChar)
231 :
232 : {
233 : char aszField[128];
234 0 : int nLength = nEndChar - nStartChar + 1;
235 :
236 0 : CPLAssert(nEndChar - nStartChar + 2 < (int)sizeof(aszField));
237 :
238 0 : strncpy(aszField, pachRawDataRecord + nStartChar - 1, nLength);
239 :
240 0 : aszField[nLength] = '\0';
241 0 : while (nLength > 0 && aszField[nLength - 1] == ' ')
242 0 : aszField[--nLength] = '\0';
243 :
244 0 : return CPLSPrintf("%s", aszField);
245 : }
246 :
247 : /************************************************************************/
248 : /* SetField() */
249 : /* */
250 : /* Set a field on an OGRFeature from a tiger record, or leave */
251 : /* NULL if the value isn't found. */
252 : /************************************************************************/
253 :
254 0 : void TigerFileBase::SetField(OGRFeature *poFeature, const char *pszField,
255 : const char *pachRecord, int nStart, int nEnd)
256 :
257 : {
258 0 : const char *pszFieldValue = GetField(pachRecord, nStart, nEnd);
259 :
260 0 : if (pszFieldValue[0] == '\0')
261 0 : return;
262 :
263 0 : poFeature->SetField(pszField, pszFieldValue);
264 : }
265 :
266 : /************************************************************************/
267 : /* AddFieldDefns() */
268 : /************************************************************************/
269 0 : void TigerFileBase::AddFieldDefns(const TigerRecordInfo *psRTInfoIn,
270 : OGRFeatureDefn *poFeatureDefnIn)
271 : {
272 0 : OGRFieldDefn oField("", OFTInteger);
273 : int i, bLFieldHack;
274 :
275 0 : bLFieldHack =
276 0 : CPLTestBool(CPLGetConfigOption("TIGER_LFIELD_AS_STRING", "NO"));
277 :
278 0 : for (i = 0; i < psRTInfoIn->nFieldCount; ++i)
279 : {
280 0 : if (psRTInfoIn->pasFields[i].bDefine)
281 : {
282 0 : OGRFieldType eFT = (OGRFieldType)psRTInfoIn->pasFields[i].OGRtype;
283 :
284 0 : if (bLFieldHack && psRTInfoIn->pasFields[i].cFmt == 'L' &&
285 0 : psRTInfoIn->pasFields[i].cType == 'N')
286 0 : eFT = OFTString;
287 :
288 0 : oField.Set(psRTInfoIn->pasFields[i].pszFieldName, eFT,
289 0 : psRTInfoIn->pasFields[i].nLen);
290 0 : poFeatureDefnIn->AddFieldDefn(&oField);
291 : }
292 : }
293 0 : }
294 :
295 : /************************************************************************/
296 : /* SetFields() */
297 : /************************************************************************/
298 :
299 0 : void TigerFileBase::SetFields(const TigerRecordInfo *psRTInfoIn,
300 : OGRFeature *poFeature, char *achRecord)
301 : {
302 0 : for (int i = 0; i < psRTInfoIn->nFieldCount; ++i)
303 : {
304 0 : if (psRTInfoIn->pasFields[i].bSet)
305 : {
306 0 : SetField(poFeature, psRTInfoIn->pasFields[i].pszFieldName,
307 0 : achRecord, psRTInfoIn->pasFields[i].nBeg,
308 0 : psRTInfoIn->pasFields[i].nEnd);
309 : }
310 : }
311 0 : }
312 :
313 : /************************************************************************/
314 : /* SetModule() */
315 : /************************************************************************/
316 :
317 0 : bool TigerFileBase::SetModule(const char *pszModuleIn)
318 :
319 : {
320 0 : if (m_pszFileCode == nullptr)
321 0 : return false;
322 :
323 0 : if (!OpenFile(pszModuleIn, m_pszFileCode))
324 0 : return false;
325 :
326 0 : EstablishFeatureCount();
327 :
328 0 : return true;
329 : }
330 :
331 : /************************************************************************/
332 : /* GetFeature() */
333 : /************************************************************************/
334 :
335 0 : OGRFeature *TigerFileBase::GetFeature(int nRecordId)
336 :
337 : {
338 : char achRecord[OGR_TIGER_RECBUF_LEN];
339 :
340 0 : if (psRTInfo == nullptr)
341 0 : return nullptr;
342 :
343 0 : if (nRecordId < 0 || nRecordId >= nFeatures)
344 : {
345 0 : CPLError(CE_Failure, CPLE_FileIO,
346 : "Request for out-of-range feature %d of %s", nRecordId,
347 : pszModule);
348 0 : return nullptr;
349 : }
350 :
351 : /* -------------------------------------------------------------------- */
352 : /* Read the raw record data from the file. */
353 : /* -------------------------------------------------------------------- */
354 0 : if (fpPrimary == nullptr)
355 0 : return nullptr;
356 :
357 : {
358 0 : const auto nOffset = static_cast<uint64_t>(nRecordId) * nRecordLength;
359 0 : if (VSIFSeekL(fpPrimary, nOffset, SEEK_SET) != 0)
360 : {
361 0 : CPLError(CE_Failure, CPLE_FileIO,
362 : "Failed to seek to %" PRIu64 " of %s", nOffset, pszModule);
363 0 : return nullptr;
364 : }
365 : }
366 :
367 : // Overflow cannot happen since psRTInfo->nRecordLength is unsigned
368 : // char and sizeof(achRecord) == OGR_TIGER_RECBUF_LEN > 255
369 0 : if (VSIFReadL(achRecord, psRTInfo->nRecordLength, 1, fpPrimary) != 1)
370 : {
371 0 : CPLError(CE_Failure, CPLE_FileIO, "Failed to read record %d of %s",
372 : nRecordId, pszModule);
373 0 : return nullptr;
374 : }
375 :
376 : /* -------------------------------------------------------------------- */
377 : /* Set fields. */
378 : /* -------------------------------------------------------------------- */
379 0 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
380 :
381 0 : SetFields(psRTInfo, poFeature, achRecord);
382 :
383 0 : return poFeature;
384 : }
|