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

Generated by: LCOV version 1.14