LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/tiger - tigercompletechain.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 175 0.0 %
Date: 2026-03-26 23:25:44 Functions: 0 7 0.0 %

          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             : }

Generated by: LCOV version 1.14