Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: TIGER/Line Translator
4 : * Purpose: Implements OGRTigerDataSource class
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam <warmerdam@pobox.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_conv.h"
14 : #include "cpl_string.h"
15 : #include "ogr_tiger.h"
16 :
17 : #include <cctype>
18 : #include <algorithm>
19 :
20 : #define DIGIT_ZERO '0'
21 :
22 : /************************************************************************/
23 : /* TigerClassifyVersion() */
24 : /************************************************************************/
25 :
26 0 : TigerVersion TigerClassifyVersion(int nVersionCode)
27 :
28 : {
29 : TigerVersion nVersion;
30 : int nYear, nMonth;
31 :
32 : /*
33 : ** TIGER Versions
34 : **
35 : ** 0000 TIGER/Line Precensus Files, 1990
36 : ** 0002 TIGER/Line Initial Voting District Codes Files, 1990
37 : ** 0003 TIGER/Line Files, 1990
38 : ** 0005 TIGER/Line Files, 1992
39 : ** 0021 TIGER/Line Files, 1994
40 : ** 0024 TIGER/Line Files, 1995
41 : ** 9706 to 9810 TIGER/Line Files, 1997
42 : ** 9812 to 9904 TIGER/Line Files, 1998
43 : ** 0006 to 0008 TIGER/Line Files, 1999
44 : ** 0010 to 0011 TIGER/Line Files, Redistricting Census 2000
45 : ** 0103 to 0108 TIGER/Line Files, Census 2000
46 : **
47 : ** 0203 to 0205 TIGER/Line Files, UA 2000
48 : ** ???? ????
49 : **
50 : ** 0206 to 0299 TIGER/Line Files, 2002
51 : ** 0300 to 0399 TIGER/Line Files, 2003
52 : ** 0400+ TIGER/Line Files, 2004 - one sample is 0405
53 : ** ????
54 : */
55 :
56 0 : nVersion = TIGER_Unknown;
57 0 : if (nVersionCode == 0)
58 0 : nVersion = TIGER_1990_Precensus;
59 0 : else if (nVersionCode == 2)
60 0 : nVersion = TIGER_1990;
61 0 : else if (nVersionCode == 3)
62 0 : nVersion = TIGER_1992;
63 0 : else if (nVersionCode == 5)
64 0 : nVersion = TIGER_1994;
65 0 : else if (nVersionCode == 21)
66 0 : nVersion = TIGER_1994;
67 0 : else if (nVersionCode == 24)
68 0 : nVersion = TIGER_1995;
69 :
70 0 : else if (nVersionCode == 9999) /* special hack, fme bug 7625 */
71 0 : nVersion = TIGER_UA2000;
72 :
73 0 : nYear = nVersionCode % 100;
74 0 : nMonth = nVersionCode / 100;
75 :
76 0 : nVersionCode = nYear * 100 + nMonth;
77 :
78 0 : if (nVersion != TIGER_Unknown)
79 : /* do nothing */;
80 0 : else if (nVersionCode >= 9706 && nVersionCode <= 9810)
81 0 : nVersion = TIGER_1997;
82 0 : else if (nVersionCode >= 9812 && nVersionCode <= 9904)
83 0 : nVersion = TIGER_1998;
84 0 : else if (nVersionCode >= 6 /*0006*/ && nVersionCode <= 8 /*0008*/)
85 0 : nVersion = TIGER_1999;
86 0 : else if (nVersionCode >= 10 /*0010*/ && nVersionCode <= 11 /*0011*/)
87 0 : nVersion = TIGER_2000_Redistricting;
88 0 : else if (nVersionCode >= 103 /*0103*/ && nVersionCode <= 108 /*0108*/)
89 0 : nVersion = TIGER_2000_Census;
90 0 : else if (nVersionCode >= 203 /*0302*/ && nVersionCode <= 205 /*0502*/)
91 0 : nVersion = TIGER_UA2000;
92 0 : else if (nVersionCode >= 210 /*1002*/ && nVersionCode <= 306 /*0603*/)
93 0 : nVersion = TIGER_2002;
94 0 : else if (nVersionCode >= 312 /*1203*/ && nVersionCode <= 403 /*0304*/)
95 0 : nVersion = TIGER_2003;
96 0 : else if (nVersionCode >= 404)
97 0 : nVersion = TIGER_2004;
98 :
99 0 : return nVersion;
100 : }
101 :
102 : /************************************************************************/
103 : /* TigerVersionString() */
104 : /************************************************************************/
105 :
106 0 : const char *TigerVersionString(TigerVersion nVersion)
107 : {
108 :
109 0 : if (nVersion == TIGER_1990_Precensus)
110 : {
111 0 : return "TIGER_1990_Precensus";
112 : }
113 0 : if (nVersion == TIGER_1990)
114 : {
115 0 : return "TIGER_1990";
116 : }
117 0 : if (nVersion == TIGER_1992)
118 : {
119 0 : return "TIGER_1992";
120 : }
121 0 : if (nVersion == TIGER_1994)
122 : {
123 0 : return "TIGER_1994";
124 : }
125 0 : if (nVersion == TIGER_1995)
126 : {
127 0 : return "TIGER_1995";
128 : }
129 0 : if (nVersion == TIGER_1997)
130 : {
131 0 : return "TIGER_1997";
132 : }
133 0 : if (nVersion == TIGER_1998)
134 : {
135 0 : return "TIGER_1998";
136 : }
137 0 : if (nVersion == TIGER_1999)
138 : {
139 0 : return "TIGER_1999";
140 : }
141 0 : if (nVersion == TIGER_2000_Redistricting)
142 : {
143 0 : return "TIGER_2000_Redistricting";
144 : }
145 0 : if (nVersion == TIGER_UA2000)
146 : {
147 0 : return "TIGER_UA2000";
148 : }
149 0 : if (nVersion == TIGER_2002)
150 : {
151 0 : return "TIGER_2002";
152 : }
153 0 : if (nVersion == TIGER_2003)
154 : {
155 0 : return "TIGER_2003";
156 : }
157 0 : if (nVersion == TIGER_2004)
158 : {
159 0 : return "TIGER_2004";
160 : }
161 0 : if (nVersion == TIGER_Unknown)
162 : {
163 0 : return "TIGER_Unknown";
164 : }
165 0 : return "???";
166 : }
167 :
168 : /************************************************************************/
169 : /* TigerCheckVersion() */
170 : /* */
171 : /* Some tiger products seem to be generated with version info */
172 : /* that doesn't match the tiger specs. We can sometimes */
173 : /* recognise the wrongness by checking the record length of */
174 : /* some well known changing files and adjusting the version */
175 : /* based on this. */
176 : /************************************************************************/
177 :
178 0 : TigerVersion OGRTigerDataSource::TigerCheckVersion(TigerVersion nOldVersion,
179 : const char *pszFilename)
180 :
181 : {
182 0 : if (nOldVersion != TIGER_2002)
183 0 : return nOldVersion;
184 :
185 0 : char *pszRTCFilename = BuildFilename(pszFilename, "C");
186 0 : VSILFILE *fp = VSIFOpenL(pszRTCFilename, "rb");
187 0 : CPLFree(pszRTCFilename);
188 :
189 0 : if (fp == nullptr)
190 0 : return nOldVersion;
191 :
192 : char szHeader[115];
193 :
194 0 : if (VSIFReadL(szHeader, sizeof(szHeader) - 1, 1, fp) < 1)
195 : {
196 0 : VSIFCloseL(fp);
197 0 : return nOldVersion;
198 : }
199 :
200 0 : VSIFCloseL(fp);
201 :
202 : /* -------------------------------------------------------------------- */
203 : /* Is the record length 112? If so, it is an older version */
204 : /* than 2002. */
205 : /* -------------------------------------------------------------------- */
206 0 : if (szHeader[112] == 10 || szHeader[112] == 13)
207 : {
208 0 : CPLDebug("TIGER",
209 : "Forcing version back to UA2000 since RTC records are short.");
210 0 : return TIGER_UA2000;
211 : }
212 :
213 0 : return nOldVersion;
214 : }
215 :
216 : /************************************************************************/
217 : /* OGRTigerDataSource() */
218 : /************************************************************************/
219 :
220 511 : OGRTigerDataSource::OGRTigerDataSource()
221 511 : : nLayers(0), papoLayers(nullptr), poSpatialRef(new OGRSpatialReference()),
222 : papszOptions(nullptr), pszPath(nullptr), nModules(0),
223 511 : papszModules(nullptr), nVersionCode(0), nVersion(TIGER_Unknown)
224 : {
225 511 : poSpatialRef->SetWellKnownGeogCS("NAD83");
226 511 : poSpatialRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
227 511 : }
228 :
229 : /************************************************************************/
230 : /* ~OGRTigerDataSource() */
231 : /************************************************************************/
232 :
233 1022 : OGRTigerDataSource::~OGRTigerDataSource()
234 :
235 : {
236 511 : for (int i = 0; i < nLayers; i++)
237 0 : delete papoLayers[i];
238 :
239 511 : CPLFree(papoLayers);
240 :
241 511 : CPLFree(pszPath);
242 :
243 511 : CSLDestroy(papszOptions);
244 :
245 511 : CSLDestroy(papszModules);
246 :
247 511 : delete poSpatialRef;
248 1022 : }
249 :
250 : /************************************************************************/
251 : /* AddLayer() */
252 : /************************************************************************/
253 :
254 0 : void OGRTigerDataSource::AddLayer(OGRTigerLayer *poNewLayer)
255 :
256 : {
257 0 : poNewLayer->SetDescription(poNewLayer->GetName());
258 0 : papoLayers = static_cast<OGRTigerLayer **>(
259 0 : CPLRealloc(papoLayers, sizeof(void *) * ++nLayers));
260 :
261 0 : papoLayers[nLayers - 1] = poNewLayer;
262 0 : }
263 :
264 : /************************************************************************/
265 : /* GetLayer() */
266 : /************************************************************************/
267 :
268 0 : OGRLayer *OGRTigerDataSource::GetLayer(int iLayer)
269 :
270 : {
271 0 : if (iLayer < 0 || iLayer >= nLayers)
272 0 : return nullptr;
273 :
274 0 : return papoLayers[iLayer];
275 : }
276 :
277 : /************************************************************************/
278 : /* GetLayer() */
279 : /************************************************************************/
280 :
281 0 : OGRLayer *OGRTigerDataSource::GetLayer(const char *pszLayerName)
282 :
283 : {
284 0 : for (int iLayer = 0; iLayer < nLayers; iLayer++)
285 : {
286 0 : if (EQUAL(papoLayers[iLayer]->GetLayerDefn()->GetName(), pszLayerName))
287 0 : return papoLayers[iLayer];
288 : }
289 :
290 0 : return nullptr;
291 : }
292 :
293 : /************************************************************************/
294 : /* GetLayerCount() */
295 : /************************************************************************/
296 :
297 0 : int OGRTigerDataSource::GetLayerCount()
298 :
299 : {
300 0 : return nLayers;
301 : }
302 :
303 : /************************************************************************/
304 : /* Open() */
305 : /************************************************************************/
306 :
307 511 : int OGRTigerDataSource::Open(const char *pszFilename, int bTestOpen,
308 : char **papszLimitedFileList)
309 :
310 : {
311 : /* -------------------------------------------------------------------- */
312 : /* Is the given path a directory or a regular file? */
313 : /* -------------------------------------------------------------------- */
314 : VSIStatBufL stat;
315 :
316 511 : if (VSIStatExL(pszFilename, &stat,
317 1022 : VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) != 0 ||
318 511 : (!VSI_ISDIR(stat.st_mode) && !VSI_ISREG(stat.st_mode)))
319 : {
320 0 : if (!bTestOpen)
321 0 : CPLError(
322 : CE_Failure, CPLE_AppDefined,
323 : "%s is neither a file or directory, Tiger access failed.\n",
324 : pszFilename);
325 :
326 0 : return FALSE;
327 : }
328 :
329 : /* -------------------------------------------------------------------- */
330 : /* Build a list of filenames we figure are Tiger files. */
331 : /* -------------------------------------------------------------------- */
332 511 : char **papszFileList = nullptr;
333 511 : if (VSI_ISREG(stat.st_mode))
334 : {
335 : char szModule[128];
336 :
337 24 : if (strlen(CPLGetFilename(pszFilename)) == 0)
338 : {
339 0 : return FALSE;
340 : }
341 :
342 24 : pszPath = CPLStrdup(CPLGetPathSafe(pszFilename).c_str());
343 :
344 24 : strncpy(szModule, CPLGetFilename(pszFilename), sizeof(szModule) - 1);
345 : /* Make sure the buffer is 0 terminated */
346 24 : szModule[sizeof(szModule) - 1] = '\0';
347 :
348 : /* And now remove last character of filename */
349 24 : szModule[strlen(szModule) - 1] = '\0';
350 :
351 24 : papszFileList = CSLAddString(papszFileList, szModule);
352 : }
353 : else
354 : {
355 487 : char **candidateFileList = VSIReadDir(pszFilename);
356 :
357 487 : pszPath = CPLStrdup(pszFilename);
358 :
359 24397 : for (int i = 0;
360 24397 : candidateFileList != nullptr && candidateFileList[i] != nullptr;
361 : i++)
362 : {
363 23910 : size_t nCandidateLen = strlen(candidateFileList[i]);
364 :
365 23910 : if (papszLimitedFileList != nullptr &&
366 0 : CSLFindString(
367 : papszLimitedFileList,
368 23910 : CPLGetBasenameSafe(candidateFileList[i]).c_str()) == -1)
369 : {
370 0 : continue;
371 : }
372 :
373 23910 : if (nCandidateLen > 4 &&
374 23044 : candidateFileList[i][nCandidateLen - 4] == '.' &&
375 6379 : candidateFileList[i][nCandidateLen - 1] == '1')
376 : {
377 : char szModule[128];
378 :
379 0 : snprintf(szModule, sizeof(szModule), "%s",
380 0 : candidateFileList[i]);
381 0 : const size_t nLen = strlen(szModule);
382 0 : if (nLen)
383 0 : szModule[nLen - 1] = '\0';
384 :
385 0 : papszFileList = CSLAddString(papszFileList, szModule);
386 : }
387 : }
388 :
389 487 : CSLDestroy(candidateFileList);
390 :
391 487 : if (CSLCount(papszFileList) == 0)
392 : {
393 487 : if (!bTestOpen)
394 0 : CPLError(CE_Failure, CPLE_OpenFailed,
395 : "No candidate Tiger files (TGR*.RT1) found in\n"
396 : "directory: %s",
397 : pszFilename);
398 487 : CSLDestroy(papszFileList);
399 487 : return FALSE;
400 : }
401 : }
402 :
403 : /* -------------------------------------------------------------------- */
404 : /* Loop over all these files trying to open them. In testopen */
405 : /* mode we first read the first 80 characters, to verify that */
406 : /* it looks like an Tiger file. Note that we don't keep the file */
407 : /* open ... we don't want to occupy a lot of file handles when */
408 : /* handling a whole directory. */
409 : /* -------------------------------------------------------------------- */
410 24 : papszModules = nullptr;
411 :
412 48 : for (int i = 0; papszFileList && papszFileList[i] != nullptr; i++)
413 : {
414 24 : if (bTestOpen || i == 0)
415 : {
416 24 : char *l_pszFilename = BuildFilename(papszFileList[i], "1");
417 :
418 24 : VSILFILE *fp = VSIFOpenL(l_pszFilename, "rb");
419 24 : CPLFree(l_pszFilename);
420 :
421 24 : if (fp == nullptr)
422 24 : continue;
423 :
424 0 : char szHeader[500] = {};
425 0 : if (VSIFReadL(szHeader, sizeof(szHeader) - 1, 1, fp) < 1)
426 : {
427 0 : VSIFCloseL(fp);
428 0 : continue;
429 : }
430 :
431 0 : VSIFCloseL(fp);
432 :
433 0 : char *pszRecStart = szHeader;
434 0 : szHeader[sizeof(szHeader) - 1] = '\0';
435 :
436 0 : bool bIsGDT = false;
437 :
438 0 : if (STARTS_WITH_CI(pszRecStart, "Copyright (C)") &&
439 0 : strstr(pszRecStart, "Geographic Data Tech") != nullptr)
440 : {
441 0 : bIsGDT = true;
442 :
443 0 : while (*pszRecStart != '\0' && *pszRecStart != 10 &&
444 0 : *pszRecStart != 13)
445 0 : pszRecStart++;
446 :
447 0 : while (*pszRecStart == 10 || *pszRecStart == 13)
448 0 : pszRecStart++;
449 : }
450 :
451 0 : if (pszRecStart[0] != '1')
452 0 : continue;
453 :
454 0 : if (!isdigit(static_cast<unsigned char>(pszRecStart[1])) ||
455 0 : !isdigit(static_cast<unsigned char>(pszRecStart[2])) ||
456 0 : !isdigit(static_cast<unsigned char>(pszRecStart[3])) ||
457 0 : !isdigit(static_cast<unsigned char>(pszRecStart[4])))
458 0 : continue;
459 :
460 0 : nVersionCode = atoi(TigerFileBase::GetField(pszRecStart, 2, 5));
461 0 : nVersion = TigerClassifyVersion(nVersionCode);
462 0 : nVersion = TigerCheckVersion(nVersion, papszFileList[i]);
463 :
464 0 : CPLDebug("OGR", "Tiger Version Code=%d, Classified as %s ",
465 : nVersionCode, TigerVersionString(nVersion));
466 :
467 0 : if (nVersionCode != 0 && nVersionCode != 2 && nVersionCode != 3 &&
468 0 : nVersionCode != 5 && nVersionCode != 21 && nVersionCode != 24 &&
469 0 : pszRecStart[3] != '9' && pszRecStart[3] != DIGIT_ZERO &&
470 0 : !bIsGDT)
471 0 : continue;
472 :
473 : // we could (and should) add a bunch more validation here.
474 : }
475 :
476 0 : papszModules = CSLAddString(papszModules, papszFileList[i]);
477 : }
478 :
479 24 : CSLDestroy(papszFileList);
480 :
481 24 : nModules = CSLCount(papszModules);
482 :
483 24 : if (nModules == 0 || papszModules == nullptr)
484 : {
485 24 : if (!bTestOpen)
486 : {
487 0 : if (VSI_ISREG(stat.st_mode))
488 0 : CPLError(CE_Failure, CPLE_OpenFailed,
489 : "No TIGER/Line files (TGR*.RT1) found in\n"
490 : "directory: %s",
491 : pszFilename);
492 : else
493 0 : CPLError(
494 : CE_Failure, CPLE_OpenFailed,
495 : "File %s does not appear to be a TIGER/Line .RT1 file.",
496 : pszFilename);
497 : }
498 :
499 24 : return FALSE;
500 : }
501 :
502 : /* -------------------------------------------------------------------- */
503 : /* Do we have a user provided version override? */
504 : /* -------------------------------------------------------------------- */
505 : const char *pszRequestedVersion =
506 0 : CPLGetConfigOption("TIGER_VERSION", nullptr);
507 0 : if (pszRequestedVersion != nullptr)
508 : {
509 :
510 0 : if (STARTS_WITH_CI(pszRequestedVersion, "TIGER_"))
511 : {
512 0 : int iCode = 1; // Used after for.
513 :
514 0 : for (; iCode < TIGER_Unknown; iCode++)
515 : {
516 0 : if (EQUAL(TigerVersionString((TigerVersion)iCode),
517 : pszRequestedVersion))
518 : {
519 0 : nVersion = (TigerVersion)iCode;
520 0 : break;
521 : }
522 : }
523 :
524 0 : if (iCode == TIGER_Unknown)
525 : {
526 0 : CPLError(CE_Failure, CPLE_AppDefined,
527 : "Failed to recognise TIGER_VERSION setting: %s",
528 : pszRequestedVersion);
529 0 : return FALSE;
530 : }
531 :
532 0 : CPLDebug("OGR", "OVERRIDE Tiger Version %s ",
533 : TigerVersionString(nVersion));
534 : }
535 : else
536 : {
537 0 : nVersionCode = atoi(pszRequestedVersion);
538 0 : nVersion = TigerClassifyVersion(nVersionCode);
539 :
540 0 : CPLDebug("OGR", "OVERRIDE Tiger Version Code=%d, Classified as %s ",
541 : nVersionCode, TigerVersionString(nVersion));
542 : }
543 : }
544 :
545 : /* -------------------------------------------------------------------- */
546 : /* Create the layers which appear to exist. */
547 : /* -------------------------------------------------------------------- */
548 : // RT1, RT2, RT3
549 0 : AddLayer(
550 0 : new OGRTigerLayer(this, new TigerCompleteChain(this, papszModules[0])));
551 :
552 : /* should we have kept track of whether we encountered an RT4 file? */
553 : // RT4
554 0 : AddLayer(new OGRTigerLayer(this, new TigerAltName(this, papszModules[0])));
555 :
556 : // RT5
557 0 : AddLayer(
558 0 : new OGRTigerLayer(this, new TigerFeatureIds(this, papszModules[0])));
559 :
560 : // RT6
561 0 : AddLayer(new OGRTigerLayer(this, new TigerZipCodes(this, papszModules[0])));
562 : // RT7
563 0 : AddLayer(
564 0 : new OGRTigerLayer(this, new TigerLandmarks(this, papszModules[0])));
565 :
566 : // RT8
567 0 : AddLayer(
568 0 : new OGRTigerLayer(this, new TigerAreaLandmarks(this, papszModules[0])));
569 :
570 : // RT9
571 0 : if (nVersion < TIGER_2002)
572 : {
573 0 : AddLayer(new OGRTigerLayer(
574 0 : this, new TigerKeyFeatures(this, papszModules[0])));
575 : }
576 :
577 : // RTA, RTS
578 0 : AddLayer(new OGRTigerLayer(this, new TigerPolygon(this, papszModules[0])));
579 :
580 : // RTB
581 0 : if (nVersion >= TIGER_2002)
582 : {
583 0 : AddLayer(new OGRTigerLayer(
584 0 : this, new TigerPolygonCorrections(this, papszModules[0])));
585 : }
586 :
587 : // RTC
588 0 : AddLayer(
589 0 : new OGRTigerLayer(this, new TigerEntityNames(this, papszModules[0])));
590 :
591 : // RTE
592 0 : if (nVersion >= TIGER_2002)
593 : {
594 0 : AddLayer(new OGRTigerLayer(
595 0 : this, new TigerPolygonEconomic(this, papszModules[0])));
596 : }
597 :
598 : // RTH
599 0 : AddLayer(
600 0 : new OGRTigerLayer(this, new TigerIDHistory(this, papszModules[0])));
601 :
602 : // RTI
603 0 : AddLayer(
604 0 : new OGRTigerLayer(this, new TigerPolyChainLink(this, papszModules[0])));
605 :
606 : // RTM
607 0 : AddLayer(new OGRTigerLayer(
608 0 : this, new TigerSpatialMetadata(this, papszModules[0])));
609 :
610 : // RTP
611 0 : AddLayer(new OGRTigerLayer(this, new TigerPIP(this, papszModules[0])));
612 :
613 : // RTR
614 0 : AddLayer(
615 0 : new OGRTigerLayer(this, new TigerTLIDRange(this, papszModules[0])));
616 :
617 : // RTT
618 0 : if (nVersion >= TIGER_2002)
619 : {
620 0 : AddLayer(new OGRTigerLayer(this,
621 0 : new TigerZeroCellID(this, papszModules[0])));
622 : }
623 :
624 : // RTU
625 0 : if (nVersion >= TIGER_2002)
626 : {
627 0 : AddLayer(
628 0 : new OGRTigerLayer(this, new TigerOverUnder(this, papszModules[0])));
629 : }
630 :
631 : // RTZ
632 0 : AddLayer(new OGRTigerLayer(this, new TigerZipPlus4(this, papszModules[0])));
633 :
634 0 : return TRUE;
635 : }
636 :
637 : /************************************************************************/
638 : /* GetOption() */
639 : /************************************************************************/
640 :
641 0 : const char *OGRTigerDataSource::GetOption(const char *pszOption)
642 :
643 : {
644 0 : return CSLFetchNameValue(papszOptions, pszOption);
645 : }
646 :
647 : /************************************************************************/
648 : /* GetModule() */
649 : /************************************************************************/
650 :
651 0 : const char *OGRTigerDataSource::GetModule(int iModule)
652 :
653 : {
654 0 : if (iModule < 0 || iModule >= nModules)
655 0 : return nullptr;
656 : else
657 0 : return papszModules[iModule];
658 : }
659 :
660 : /************************************************************************/
661 : /* CheckModule() */
662 : /* */
663 : /* This is used by the writer to check if this module has been */
664 : /* written to before. */
665 : /************************************************************************/
666 :
667 0 : bool OGRTigerDataSource::CheckModule(const char *pszModule)
668 :
669 : {
670 0 : for (int i = 0; i < nModules; i++)
671 : {
672 0 : if (EQUAL(pszModule, papszModules[i]))
673 0 : return true;
674 : }
675 0 : return false;
676 : }
677 :
678 : /************************************************************************/
679 : /* AddModule() */
680 : /************************************************************************/
681 :
682 0 : void OGRTigerDataSource::AddModule(const char *pszModule)
683 :
684 : {
685 0 : if (CheckModule(pszModule))
686 0 : return;
687 :
688 0 : papszModules = CSLAddString(papszModules, pszModule);
689 0 : nModules++;
690 : }
691 :
692 : /************************************************************************/
693 : /* BuildFilename() */
694 : /************************************************************************/
695 :
696 24 : char *OGRTigerDataSource::BuildFilename(const char *pszModuleName,
697 : const char *pszExtension)
698 :
699 : {
700 : /* -------------------------------------------------------------------- */
701 : /* Force the record type to lower case if the filename appears */
702 : /* to be in lower case. */
703 : /* -------------------------------------------------------------------- */
704 24 : char szLCExtension[3] = {};
705 24 : if (*pszExtension >= 'A' && *pszExtension <= 'Z' && *pszModuleName == 't')
706 : {
707 0 : szLCExtension[0] = (*pszExtension) + 'a' - 'A';
708 0 : szLCExtension[1] = '\0';
709 0 : pszExtension = szLCExtension;
710 : }
711 :
712 : /* -------------------------------------------------------------------- */
713 : /* Build the filename. */
714 : /* -------------------------------------------------------------------- */
715 24 : const size_t nFilenameLen = strlen(GetDirPath()) + strlen(pszModuleName) +
716 24 : strlen(pszExtension) + 10;
717 24 : char *pszFilename = (char *)CPLMalloc(nFilenameLen);
718 :
719 24 : if (strlen(GetDirPath()) == 0)
720 0 : snprintf(pszFilename, nFilenameLen, "%s%s", pszModuleName,
721 : pszExtension);
722 : else
723 24 : snprintf(pszFilename, nFilenameLen, "%s/%s%s", GetDirPath(),
724 : pszModuleName, pszExtension);
725 :
726 24 : return pszFilename;
727 : }
|