LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/ntf - ntffilereader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 911 0.0 %
Date: 2024-11-25 13:07:18 Functions: 0 39 0.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  NTF Translator
       4             :  * Purpose:  NTFFileReader class implementation.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include <stdarg.h>
      15             : #include "ntf.h"
      16             : #include "cpl_conv.h"
      17             : #include "cpl_string.h"
      18             : #include "ogr_api.h"
      19             : 
      20             : #include <algorithm>
      21             : 
      22             : #define DIGIT_ZERO '0'
      23             : 
      24             : static int DefaultNTFRecordGrouper(NTFFileReader *, NTFRecord **, NTFRecord *);
      25             : 
      26             : /************************************************************************/
      27             : /*                            NTFFileReader                             */
      28             : /************************************************************************/
      29             : 
      30           0 : NTFFileReader::NTFFileReader(OGRNTFDataSource *poDataSource)
      31             :     : pszFilename(nullptr), poDS(poDataSource), fp(nullptr), nFCCount(0),
      32             :       papszFCNum(nullptr), papszFCName(nullptr), nAttCount(0),
      33             :       pasAttDesc(nullptr), pszTileName(nullptr), nCoordWidth(6), nZWidth(6),
      34             :       nNTFLevel(0), dfXYMult(1.0), dfZMult(1.0), dfXOrigin(0), dfYOrigin(0),
      35             :       dfTileXSize(0), dfTileYSize(0), dfScale(0.0), dfPaperToGround(0.0),
      36             :       nStartPos(0), nPreSavedPos(0), nPostSavedPos(0), poSavedRecord(nullptr),
      37             :       nSavedFeatureId(1), nBaseFeatureId(1), nFeatureCount(-1),
      38             :       pszProduct(nullptr), pszPVName(nullptr), nProduct(NPC_UNKNOWN),
      39             :       pfnRecordGrouper(DefaultNTFRecordGrouper), bIndexBuilt(FALSE),
      40             :       bIndexNeeded(FALSE), nRasterXSize(1), nRasterYSize(1), nRasterDataType(1),
      41             :       poRasterLayer(nullptr), panColumnOffset(nullptr), bCacheLines(TRUE),
      42           0 :       nLineCacheSize(0), papoLineCache(nullptr)
      43             : {
      44           0 :     apoCGroup[0] = nullptr;
      45           0 :     apoCGroup[1] = nullptr;
      46           0 :     apoCGroup[MAX_REC_GROUP] = nullptr;
      47           0 :     memset(adfGeoTransform, 0, sizeof(adfGeoTransform));
      48           0 :     memset(apoTypeTranslation, 0, sizeof(apoTypeTranslation));
      49           0 :     for (int i = 0; i < 100; i++)
      50             :     {
      51           0 :         anIndexSize[i] = 0;
      52           0 :         apapoRecordIndex[i] = nullptr;
      53             :     }
      54           0 :     if (poDS->GetOption("CACHE_LINES") != nullptr &&
      55           0 :         EQUAL(poDS->GetOption("CACHE_LINES"), "OFF"))
      56           0 :         bCacheLines = FALSE;
      57           0 : }
      58             : 
      59             : /************************************************************************/
      60             : /*                           ~NTFFileReader()                           */
      61             : /************************************************************************/
      62             : 
      63           0 : NTFFileReader::~NTFFileReader()
      64             : 
      65             : {
      66           0 :     CacheClean();
      67           0 :     DestroyIndex();
      68           0 :     ClearDefs();
      69           0 :     CPLFree(pszFilename);
      70           0 :     CPLFree(panColumnOffset);
      71           0 : }
      72             : 
      73             : /************************************************************************/
      74             : /*                             SetBaseFID()                             */
      75             : /************************************************************************/
      76             : 
      77           0 : void NTFFileReader::SetBaseFID(long nNewBase)
      78             : 
      79             : {
      80           0 :     CPLAssert(nSavedFeatureId == 1);
      81             : 
      82           0 :     nBaseFeatureId = nNewBase;
      83           0 :     nSavedFeatureId = nBaseFeatureId;
      84           0 : }
      85             : 
      86             : /************************************************************************/
      87             : /*                             ClearDefs()                              */
      88             : /*                                                                      */
      89             : /*      Clear attribute definitions and feature classes.  All the       */
      90             : /*      stuff that would have to be cleaned up by Open(), and the       */
      91             : /*      destructor.                                                     */
      92             : /************************************************************************/
      93             : 
      94           0 : void NTFFileReader::ClearDefs()
      95             : 
      96             : {
      97           0 :     Close();
      98             : 
      99           0 :     ClearCGroup();
     100             : 
     101           0 :     CSLDestroy(papszFCNum);
     102           0 :     papszFCNum = nullptr;
     103           0 :     CSLDestroy(papszFCName);
     104           0 :     papszFCName = nullptr;
     105           0 :     nFCCount = 0;
     106             : 
     107           0 :     for (int i = 0; i < nAttCount; i++)
     108             :     {
     109           0 :         if (pasAttDesc[i].poCodeList != nullptr)
     110           0 :             delete pasAttDesc[i].poCodeList;
     111             :     }
     112             : 
     113           0 :     CPLFree(pasAttDesc);
     114           0 :     nAttCount = 0;
     115           0 :     pasAttDesc = nullptr;
     116             : 
     117           0 :     CPLFree(pszProduct);
     118           0 :     pszProduct = nullptr;
     119             : 
     120           0 :     CPLFree(pszPVName);
     121           0 :     pszPVName = nullptr;
     122             : 
     123           0 :     CPLFree(pszTileName);
     124           0 :     pszTileName = nullptr;
     125           0 : }
     126             : 
     127             : /************************************************************************/
     128             : /*                               Close()                                */
     129             : /*                                                                      */
     130             : /*      Close the file, but don't wipe out our knowledge about this     */
     131             : /*      file.                                                           */
     132             : /************************************************************************/
     133             : 
     134           0 : void NTFFileReader::Close()
     135             : 
     136             : {
     137           0 :     if (poSavedRecord != nullptr)
     138           0 :         delete poSavedRecord;
     139           0 :     poSavedRecord = nullptr;
     140             : 
     141           0 :     nPreSavedPos = nPostSavedPos = 0;
     142           0 :     nSavedFeatureId = nBaseFeatureId;
     143           0 :     if (fp != nullptr)
     144             :     {
     145           0 :         VSIFCloseL(fp);
     146           0 :         fp = nullptr;
     147             :     }
     148             : 
     149           0 :     CacheClean();
     150           0 : }
     151             : 
     152             : /************************************************************************/
     153             : /*                                Open()                                */
     154             : /************************************************************************/
     155             : 
     156           0 : int NTFFileReader::Open(const char *pszFilenameIn)
     157             : 
     158             : {
     159           0 :     if (pszFilenameIn != nullptr)
     160             :     {
     161           0 :         ClearDefs();
     162             : 
     163           0 :         CPLFree(pszFilename);
     164           0 :         pszFilename = CPLStrdup(pszFilenameIn);
     165             :     }
     166             :     else
     167           0 :         Close();
     168             : 
     169             :     /* -------------------------------------------------------------------- */
     170             :     /*      Open the file.                                                  */
     171             :     /* -------------------------------------------------------------------- */
     172           0 :     fp = VSIFOpenL(pszFilename, "rb");
     173             : 
     174             :     // notdef: we should likely issue a proper CPL error message based
     175             :     // based on errno here.
     176           0 :     if (fp == nullptr)
     177             :     {
     178           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     179             :                  "Unable to open file `%s' for read access.\n", pszFilename);
     180           0 :         return FALSE;
     181             :     }
     182             : 
     183             :     /* -------------------------------------------------------------------- */
     184             :     /*      If we are just reopening an existing file we will just scan     */
     185             :     /*      past the section header ... no need to reform all the definitions.*/
     186             :     /* -------------------------------------------------------------------- */
     187           0 :     if (pszFilenameIn == nullptr)
     188             :     {
     189           0 :         NTFRecord *poRecord = nullptr;
     190             : 
     191           0 :         for (poRecord = new NTFRecord(fp);
     192           0 :              poRecord->GetType() != NRT_VTR && poRecord->GetType() != NRT_SHR;
     193           0 :              poRecord = new NTFRecord(fp))
     194             :         {
     195           0 :             delete poRecord;
     196             :         }
     197             : 
     198           0 :         delete poRecord;
     199             : 
     200           0 :         return TRUE;
     201             :     }
     202             : 
     203             :     /* -------------------------------------------------------------------- */
     204             :     /*      Read the first record, and verify it is a proper volume header. */
     205             :     /* -------------------------------------------------------------------- */
     206           0 :     NTFRecord oVHR(fp);
     207             : 
     208           0 :     if (oVHR.GetType() != NRT_VHR)
     209             :     {
     210           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     211             :                  "File `%s' appears to not be a UK NTF file.\n", pszFilename);
     212           0 :         return FALSE;
     213             :     }
     214             : 
     215           0 :     nNTFLevel = atoi(oVHR.GetField(57, 57));
     216           0 :     if (!(nNTFLevel >= 1 && nNTFLevel <= 5))
     217             :     {
     218           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid value : nNTFLevel = %d",
     219             :                  nNTFLevel);
     220           0 :         return FALSE;
     221             :     }
     222             : 
     223             :     /* -------------------------------------------------------------------- */
     224             :     /*      Read records till we get the section header.                    */
     225             :     /* -------------------------------------------------------------------- */
     226           0 :     NTFRecord *poRecord = nullptr;
     227             : 
     228           0 :     for (poRecord = new NTFRecord(fp);
     229           0 :          poRecord->GetType() != NRT_VTR && poRecord->GetType() != NRT_SHR;
     230           0 :          poRecord = new NTFRecord(fp))
     231             :     {
     232             :         /* --------------------------------------------------------------------
     233             :          */
     234             :         /*      Handle feature class name records. */
     235             :         /* --------------------------------------------------------------------
     236             :          */
     237           0 :         if (poRecord->GetType() == NRT_FCR && poRecord->GetLength() >= 37)
     238             :         {
     239           0 :             nFCCount++;
     240             : 
     241           0 :             papszFCNum = CSLAddString(papszFCNum, poRecord->GetField(3, 6));
     242             : 
     243           0 :             CPLString osFCName;
     244           0 :             const char *pszData = poRecord->GetData();
     245             : 
     246             :             // CODE_COM
     247           0 :             int iChar = 15;
     248           0 :             for (; pszData[iChar] == ' ' && iChar > 5; iChar--)
     249             :             {
     250             :             }
     251             : 
     252           0 :             if (iChar > 6)
     253           0 :                 osFCName += poRecord->GetField(7, iChar + 1);
     254             : 
     255             :             // STCLASS
     256           0 :             for (iChar = 35; pszData[iChar] == ' ' && iChar > 15; iChar--)
     257             :             {
     258             :             }
     259             : 
     260           0 :             if (iChar > 15)
     261             :             {
     262           0 :                 if (!osFCName.empty())
     263           0 :                     osFCName += " : ";
     264           0 :                 osFCName += poRecord->GetField(17, iChar + 1);
     265             :             }
     266             : 
     267             :             // FEATDES
     268           0 :             for (iChar = 36; pszData[iChar] != '\0' && pszData[iChar] != '\\';
     269             :                  iChar++)
     270             :             {
     271             :             }
     272             : 
     273           0 :             if (iChar > 37)
     274             :             {
     275           0 :                 if (!osFCName.empty())
     276           0 :                     osFCName += " : ";
     277           0 :                 osFCName += poRecord->GetField(37, iChar);
     278             :             }
     279             : 
     280           0 :             papszFCName = CSLAddString(papszFCName, osFCName);
     281             :         }
     282             : 
     283             :         /* --------------------------------------------------------------------
     284             :          */
     285             :         /*      Handle attribute description records. */
     286             :         /* --------------------------------------------------------------------
     287             :          */
     288           0 :         else if (poRecord->GetType() == NRT_ADR)
     289             :         {
     290           0 :             nAttCount++;
     291             : 
     292           0 :             pasAttDesc = static_cast<NTFAttDesc *>(
     293           0 :                 CPLRealloc(pasAttDesc, sizeof(NTFAttDesc) * nAttCount));
     294           0 :             memset(&pasAttDesc[nAttCount - 1], 0, sizeof(NTFAttDesc));
     295             : 
     296           0 :             if (!ProcessAttDesc(poRecord, pasAttDesc + nAttCount - 1))
     297           0 :                 nAttCount--;
     298             :         }
     299             : 
     300             :         /* --------------------------------------------------------------------
     301             :          */
     302             :         /*      Handle attribute description records. */
     303             :         /* --------------------------------------------------------------------
     304             :          */
     305           0 :         else if (poRecord->GetType() == NRT_CODELIST)
     306             :         {
     307           0 :             NTFCodeList *poCodeList = new NTFCodeList(poRecord);
     308           0 :             NTFAttDesc *psAttDesc = GetAttDesc(poCodeList->szValType);
     309           0 :             if (psAttDesc == nullptr)
     310             :             {
     311           0 :                 CPLDebug("NTF", "Got CODELIST for %s without ATTDESC.",
     312           0 :                          poCodeList->szValType);
     313           0 :                 delete poCodeList;
     314             :             }
     315           0 :             else if (psAttDesc->poCodeList != nullptr)
     316             :             {
     317             :                 // Should not happen on sane files.
     318           0 :                 delete poCodeList;
     319             :             }
     320             :             else
     321             :             {
     322           0 :                 psAttDesc->poCodeList = poCodeList;
     323             :             }
     324             :         }
     325             : 
     326             :         /* --------------------------------------------------------------------
     327             :          */
     328             :         /*      Handle database header record. */
     329             :         /* --------------------------------------------------------------------
     330             :          */
     331           0 :         else if (poRecord->GetType() == NRT_DHR && pszProduct == nullptr)
     332             :         {
     333           0 :             pszProduct = CPLStrdup(poRecord->GetField(3, 22));
     334           0 :             for (int iChar = static_cast<int>(strlen(pszProduct)) - 1;
     335           0 :                  iChar > 0 && pszProduct[iChar] == ' ';
     336           0 :                  pszProduct[iChar--] = '\0')
     337             :             {
     338             :             }
     339             : 
     340           0 :             pszPVName = CPLStrdup(poRecord->GetField(76 + 3, 76 + 22));
     341           0 :             for (int iChar = static_cast<int>(strlen(pszPVName)) - 1;
     342           0 :                  iChar > 0 && pszPVName[iChar] == ' ';
     343           0 :                  pszPVName[iChar--] = '\0')
     344             :             {
     345             :             }
     346             :         }
     347             : 
     348           0 :         delete poRecord;
     349             :     }
     350             : 
     351             :     /* -------------------------------------------------------------------- */
     352             :     /*      Did we fall off the end without finding what we were looking    */
     353             :     /*      for?                                                            */
     354             :     /* -------------------------------------------------------------------- */
     355           0 :     if (poRecord->GetType() == NRT_VTR)
     356             :     {
     357           0 :         delete poRecord;
     358           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     359             :                  "Could not find section header record in %s.\n", pszFilename);
     360           0 :         return FALSE;
     361             :     }
     362             : 
     363           0 :     if (pszProduct == nullptr)
     364             :     {
     365           0 :         delete poRecord;
     366           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     367             :                  "Could not find product type in %s.\n", pszFilename);
     368           0 :         return FALSE;
     369             :     }
     370             : 
     371             :     /* -------------------------------------------------------------------- */
     372             :     /*      Classify the product type.                                      */
     373             :     /* -------------------------------------------------------------------- */
     374           0 :     if (STARTS_WITH_CI(pszProduct, "LAND-LINE") && strlen(pszPVName) > 5 &&
     375           0 :         CPLAtof(pszPVName + 5) < 1.3)
     376           0 :         nProduct = NPC_LANDLINE;
     377           0 :     else if (STARTS_WITH_CI(pszProduct, "LAND-LINE"))
     378           0 :         nProduct = NPC_LANDLINE99;
     379           0 :     else if (EQUAL(pszProduct, "OS_LANDRANGER_CONT"))  // Panorama
     380           0 :         nProduct = NPC_LANDRANGER_CONT;
     381           0 :     else if (EQUAL(pszProduct, "L-F_PROFILE_CON"))  // Panorama
     382           0 :         nProduct = NPC_LANDFORM_PROFILE_CONT;
     383           0 :     else if (STARTS_WITH_CI(pszProduct, "Strategi"))
     384           0 :         nProduct = NPC_STRATEGI;
     385           0 :     else if (STARTS_WITH_CI(pszProduct, "Meridian_02"))
     386           0 :         nProduct = NPC_MERIDIAN2;
     387           0 :     else if (STARTS_WITH_CI(pszProduct, "Meridian_01"))
     388           0 :         nProduct = NPC_MERIDIAN;
     389           0 :     else if (EQUAL(pszProduct, NTF_BOUNDARYLINE) &&
     390           0 :              STARTS_WITH_CI(pszPVName, "A10N_FC"))
     391           0 :         nProduct = NPC_BOUNDARYLINE;
     392           0 :     else if (EQUAL(pszProduct, NTF_BOUNDARYLINE) &&
     393           0 :              STARTS_WITH_CI(pszPVName, "A20N_FC"))
     394           0 :         nProduct = NPC_BL2000;
     395           0 :     else if (STARTS_WITH_CI(pszProduct, "BaseData.GB"))
     396           0 :         nProduct = NPC_BASEDATA;
     397           0 :     else if (STARTS_WITH_CI(pszProduct, "OSCAR_ASSET"))
     398           0 :         nProduct = NPC_OSCAR_ASSET;
     399           0 :     else if (STARTS_WITH_CI(pszProduct, "OSCAR_TRAFF"))
     400           0 :         nProduct = NPC_OSCAR_TRAFFIC;
     401           0 :     else if (STARTS_WITH_CI(pszProduct, "OSCAR_ROUTE"))
     402           0 :         nProduct = NPC_OSCAR_ROUTE;
     403           0 :     else if (STARTS_WITH_CI(pszProduct, "OSCAR_NETWO"))
     404           0 :         nProduct = NPC_OSCAR_NETWORK;
     405           0 :     else if (STARTS_WITH_CI(pszProduct, "ADDRESS_POI"))
     406           0 :         nProduct = NPC_ADDRESS_POINT;
     407           0 :     else if (STARTS_WITH_CI(pszProduct, "CODE_POINT"))
     408             :     {
     409           0 :         if (GetAttDesc("RH") == nullptr)
     410           0 :             nProduct = NPC_CODE_POINT;
     411             :         else
     412           0 :             nProduct = NPC_CODE_POINT_PLUS;
     413             :     }
     414           0 :     else if (STARTS_WITH_CI(pszProduct, "OS_LANDRANGER_DTM"))
     415           0 :         nProduct = NPC_LANDRANGER_DTM;
     416           0 :     else if (STARTS_WITH_CI(pszProduct, "L-F_PROFILE_DTM"))
     417           0 :         nProduct = NPC_LANDFORM_PROFILE_DTM;
     418           0 :     else if (STARTS_WITH_CI(pszProduct, "NEXTMap Britain DTM"))
     419           0 :         nProduct = NPC_LANDFORM_PROFILE_DTM;  // Treat as landform
     420             : 
     421           0 :     if (poDS->GetOption("FORCE_GENERIC") != nullptr &&
     422           0 :         !EQUAL(poDS->GetOption("FORCE_GENERIC"), "OFF"))
     423           0 :         nProduct = NPC_UNKNOWN;
     424             : 
     425             :     // No point in caching lines if there are no polygons.
     426           0 :     if (nProduct != NPC_BOUNDARYLINE && nProduct != NPC_BL2000)
     427           0 :         bCacheLines = FALSE;
     428             : 
     429             :     /* -------------------------------------------------------------------- */
     430             :     /*      Handle the section header record.                               */
     431             :     /* -------------------------------------------------------------------- */
     432           0 :     nSavedFeatureId = nBaseFeatureId;
     433           0 :     nStartPos = VSIFTellL(fp);
     434             : 
     435           0 :     pszTileName = CPLStrdup(poRecord->GetField(3, 12));  // SECT_REF
     436           0 :     size_t nTileNameLen = strlen(pszTileName);
     437           0 :     while (nTileNameLen > 0 && pszTileName[nTileNameLen - 1] == ' ')
     438             :     {
     439           0 :         pszTileName[nTileNameLen - 1] = '\0';
     440           0 :         nTileNameLen--;
     441             :     }
     442             : 
     443           0 :     nCoordWidth = atoi(poRecord->GetField(15, 19));  // XYLEN
     444           0 :     if (nCoordWidth <= 0)
     445           0 :         nCoordWidth = 10;
     446             : 
     447           0 :     nZWidth = atoi(poRecord->GetField(31, 35));  // ZLEN
     448           0 :     if (nZWidth <= 0)
     449           0 :         nZWidth = 10;
     450             : 
     451           0 :     dfXYMult = atoi(poRecord->GetField(21, 30)) / 1000.0;  // XY_MULT
     452           0 :     dfXOrigin = atoi(poRecord->GetField(47, 56));
     453           0 :     dfYOrigin = atoi(poRecord->GetField(57, 66));
     454           0 :     dfTileXSize = atoi(poRecord->GetField(23 + 74, 32 + 74));
     455           0 :     dfTileYSize = atoi(poRecord->GetField(33 + 74, 42 + 74));
     456           0 :     dfZMult = atoi(poRecord->GetField(37, 46)) / 1000.0;
     457             : 
     458             :     /* -------------------------------------------------------------------- */
     459             :     /*      Setup scale and transformation factor for text height.          */
     460             :     /* -------------------------------------------------------------------- */
     461           0 :     if (poRecord->GetLength() >= 187)
     462           0 :         dfScale = atoi(poRecord->GetField(148 + 31, 148 + 39));
     463           0 :     else if (nProduct == NPC_STRATEGI)
     464           0 :         dfScale = 250000;
     465           0 :     else if (nProduct == NPC_MERIDIAN || nProduct == NPC_MERIDIAN2)
     466           0 :         dfScale = 100000;
     467           0 :     else if (nProduct == NPC_LANDFORM_PROFILE_CONT)
     468           0 :         dfScale = 10000;
     469           0 :     else if (nProduct == NPC_LANDRANGER_CONT)
     470           0 :         dfScale = 50000;
     471           0 :     else if (nProduct == NPC_OSCAR_ASSET || nProduct == NPC_OSCAR_TRAFFIC ||
     472           0 :              nProduct == NPC_OSCAR_NETWORK || nProduct == NPC_OSCAR_ROUTE)
     473           0 :         dfScale = 10000;
     474           0 :     else if (nProduct == NPC_BASEDATA)
     475           0 :         dfScale = 625000;
     476             :     else /*if( nProduct == NPC_BOUNDARYLINE ) or default case */
     477           0 :         dfScale = 10000;
     478             : 
     479           0 :     if (dfScale != 0.0)
     480           0 :         dfPaperToGround = dfScale / 1000.0;
     481             :     else
     482           0 :         dfPaperToGround = 0.0;
     483             : 
     484           0 :     delete poRecord;
     485             : 
     486             :     /* -------------------------------------------------------------------- */
     487             :     /*      Ensure we have appropriate layers defined.                      */
     488             :     /* -------------------------------------------------------------------- */
     489           0 :     CPLErrorReset();
     490             : 
     491           0 :     if (!IsRasterProduct())
     492           0 :         EstablishLayers();
     493             :     else
     494           0 :         EstablishRasterAccess();
     495             : 
     496           0 :     return CPLGetLastErrorType() != CE_Failure;
     497             : }
     498             : 
     499             : /************************************************************************/
     500             : /*                            DumpReadable()                            */
     501             : /************************************************************************/
     502             : 
     503           0 : void NTFFileReader::DumpReadable(FILE *fpLog)
     504             : 
     505             : {
     506           0 :     fprintf(fpLog, "Tile Name = %s\n", pszTileName);
     507           0 :     fprintf(fpLog, "Product = %s\n", pszProduct);
     508           0 :     fprintf(fpLog, "NTFLevel = %d\n", nNTFLevel);
     509           0 :     fprintf(fpLog, "XYLEN = %d\n", nCoordWidth);
     510           0 :     fprintf(fpLog, "XY_MULT = %g\n", dfXYMult);
     511           0 :     fprintf(fpLog, "X_ORIG = %g\n", dfXOrigin);
     512           0 :     fprintf(fpLog, "Y_ORIG = %g\n", dfYOrigin);
     513           0 :     fprintf(fpLog, "XMAX = %g\n", dfTileXSize);
     514           0 :     fprintf(fpLog, "YMAX = %g\n", dfTileYSize);
     515           0 : }
     516             : 
     517             : /************************************************************************/
     518             : /*                          ProcessGeometry()                           */
     519             : /*                                                                      */
     520             : /*      Drop duplicate vertices from line strings ... they mess up      */
     521             : /*      FME's polygon handling sometimes.                               */
     522             : /************************************************************************/
     523             : 
     524           0 : OGRGeometry *NTFFileReader::ProcessGeometry(NTFRecord *poRecord, int *pnGeomId)
     525             : 
     526             : {
     527           0 :     if (poRecord->GetType() == NRT_GEOMETRY3D)
     528           0 :         return ProcessGeometry3D(poRecord, pnGeomId);
     529             : 
     530           0 :     else if (poRecord->GetType() != NRT_GEOMETRY)
     531           0 :         return nullptr;
     532             : 
     533           0 :     const int nGType = atoi(poRecord->GetField(9, 9));       // GTYPE
     534           0 :     const int nNumCoord = atoi(poRecord->GetField(10, 13));  // NUM_COORD
     535           0 :     if (nNumCoord < 0)
     536           0 :         return nullptr;
     537           0 :     if (pnGeomId != nullptr)
     538           0 :         *pnGeomId = atoi(poRecord->GetField(3, 8));  // GEOM_ID
     539             : 
     540             :     /* -------------------------------------------------------------------- */
     541             :     /*      Point                                                           */
     542             :     /* -------------------------------------------------------------------- */
     543           0 :     OGRGeometry *poGeometry = nullptr;
     544           0 :     if (nGType == 1)
     545             :     {
     546             :         const double dfX =
     547           0 :             atoi(poRecord->GetField(14, 14 + GetXYLen() - 1)) * GetXYMult() +
     548           0 :             GetXOrigin();
     549             :         const double dfY =
     550           0 :             atoi(poRecord->GetField(14 + GetXYLen(), 14 + GetXYLen() * 2 - 1)) *
     551           0 :                 GetXYMult() +
     552           0 :             GetYOrigin();
     553             : 
     554           0 :         poGeometry = new OGRPoint(dfX, dfY);
     555             :     }
     556             : 
     557             :     /* -------------------------------------------------------------------- */
     558             :     /*      Line (or arc)                                                   */
     559             :     /* -------------------------------------------------------------------- */
     560           0 :     else if (nGType == 2 || nGType == 3 || nGType == 4)
     561             :     {
     562             : 
     563           0 :         if (nNumCoord > 0 && poRecord->GetLength() <
     564           0 :                                  14 + (nNumCoord - 1) * (GetXYLen() * 2 + 1) +
     565           0 :                                      GetXYLen() * 2 - 1)
     566             :         {
     567           0 :             return nullptr;
     568             :         }
     569             : 
     570           0 :         OGRLineString *poLine = new OGRLineString;
     571           0 :         double dfXLast = 0.0;
     572           0 :         double dfYLast = 0.0;
     573           0 :         int nOutCount = 0;
     574             : 
     575           0 :         poGeometry = poLine;
     576           0 :         poLine->setNumPoints(nNumCoord);
     577           0 :         for (int iCoord = 0; iCoord < nNumCoord; iCoord++)
     578             :         {
     579           0 :             const int iStart = 14 + iCoord * (GetXYLen() * 2 + 1);
     580             : 
     581             :             const double dfX =
     582           0 :                 atoi(poRecord->GetField(iStart + 0, iStart + GetXYLen() - 1)) *
     583           0 :                     GetXYMult() +
     584           0 :                 GetXOrigin();
     585             :             const double dfY =
     586           0 :                 atoi(poRecord->GetField(iStart + GetXYLen(),
     587           0 :                                         iStart + GetXYLen() * 2 - 1)) *
     588           0 :                     GetXYMult() +
     589           0 :                 GetYOrigin();
     590             : 
     591           0 :             if (iCoord == 0)
     592             :             {
     593           0 :                 dfXLast = dfX;
     594           0 :                 dfYLast = dfY;
     595           0 :                 poLine->setPoint(nOutCount++, dfX, dfY);
     596             :             }
     597           0 :             else if (dfXLast != dfX || dfYLast != dfY)
     598             :             {
     599           0 :                 dfXLast = dfX;
     600           0 :                 dfYLast = dfY;
     601           0 :                 poLine->setPoint(nOutCount++, dfX, dfY);
     602             :             }
     603             :         }
     604           0 :         poLine->setNumPoints(nOutCount);
     605             : 
     606           0 :         CacheAddByGeomId(atoi(poRecord->GetField(3, 8)), poLine);
     607             :     }
     608             : 
     609             :     /* -------------------------------------------------------------------- */
     610             :     /*      Arc defined by three points on the arc.                         */
     611             :     /* -------------------------------------------------------------------- */
     612           0 :     else if (nGType == 5 && nNumCoord == 3)
     613             :     {
     614           0 :         double adfX[3] = {0.0, 0.0, 0.0};
     615           0 :         double adfY[3] = {0.0, 0.0, 0.0};
     616             : 
     617           0 :         for (int iCoord = 0; iCoord < nNumCoord; iCoord++)
     618             :         {
     619           0 :             const int iStart = 14 + iCoord * (GetXYLen() * 2 + 1);
     620             : 
     621           0 :             adfX[iCoord] =
     622           0 :                 atoi(poRecord->GetField(iStart + 0, iStart + GetXYLen() - 1)) *
     623           0 :                     GetXYMult() +
     624           0 :                 GetXOrigin();
     625           0 :             adfY[iCoord] =
     626           0 :                 atoi(poRecord->GetField(iStart + GetXYLen(),
     627           0 :                                         iStart + GetXYLen() * 2 - 1)) *
     628           0 :                     GetXYMult() +
     629           0 :                 GetYOrigin();
     630             :         }
     631             : 
     632           0 :         poGeometry = NTFStrokeArcToOGRGeometry_Points(
     633           0 :             adfX[0], adfY[0], adfX[1], adfY[1], adfX[2], adfY[2], 72);
     634             :     }
     635             : 
     636             :     /* -------------------------------------------------------------------- */
     637             :     /*      Circle                                                          */
     638             :     /* -------------------------------------------------------------------- */
     639           0 :     else if (nGType == 7)
     640             :     {
     641           0 :         const int iCenterStart = 14;
     642           0 :         const int iArcStart = 14 + 2 * GetXYLen() + 1;
     643             : 
     644             :         const double dfCenterX =
     645           0 :             atoi(poRecord->GetField(iCenterStart,
     646           0 :                                     iCenterStart + GetXYLen() - 1)) *
     647           0 :                 GetXYMult() +
     648           0 :             GetXOrigin();
     649             :         const double dfCenterY =
     650           0 :             atoi(poRecord->GetField(iCenterStart + GetXYLen(),
     651           0 :                                     iCenterStart + GetXYLen() * 2 - 1)) *
     652           0 :                 GetXYMult() +
     653           0 :             GetYOrigin();
     654             : 
     655             :         const double dfArcX =
     656           0 :             atoi(poRecord->GetField(iArcStart, iArcStart + GetXYLen() - 1)) *
     657           0 :                 GetXYMult() +
     658           0 :             GetXOrigin();
     659             :         const double dfArcY =
     660           0 :             atoi(poRecord->GetField(iArcStart + GetXYLen(),
     661           0 :                                     iArcStart + GetXYLen() * 2 - 1)) *
     662           0 :                 GetXYMult() +
     663           0 :             GetYOrigin();
     664             : 
     665             :         const double dfRadius =
     666           0 :             sqrt((dfCenterX - dfArcX) * (dfCenterX - dfArcX) +
     667           0 :                  (dfCenterY - dfArcY) * (dfCenterY - dfArcY));
     668             : 
     669           0 :         poGeometry = NTFStrokeArcToOGRGeometry_Angles(dfCenterX, dfCenterY,
     670             :                                                       dfRadius, 0.0, 360.0, 72);
     671             :     }
     672             : 
     673             :     else
     674             :     {
     675           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unhandled GType = %d", nGType);
     676             :     }
     677             : 
     678           0 :     if (poGeometry != nullptr)
     679           0 :         poGeometry->assignSpatialReference(poDS->DSGetSpatialRef());
     680             : 
     681           0 :     return poGeometry;
     682             : }
     683             : 
     684             : /************************************************************************/
     685             : /*                         ProcessGeometry3D()                          */
     686             : /************************************************************************/
     687             : 
     688           0 : OGRGeometry *NTFFileReader::ProcessGeometry3D(NTFRecord *poRecord,
     689             :                                               int *pnGeomId)
     690             : 
     691             : {
     692           0 :     OGRGeometry *poGeometry = nullptr;
     693             : 
     694           0 :     if (poRecord->GetType() != NRT_GEOMETRY3D)
     695           0 :         return nullptr;
     696             : 
     697           0 :     const int nGType = atoi(poRecord->GetField(9, 9));       // GTYPE
     698           0 :     const int nNumCoord = atoi(poRecord->GetField(10, 13));  // NUM_COORD
     699           0 :     if (pnGeomId != nullptr)
     700           0 :         *pnGeomId = atoi(poRecord->GetField(3, 8));  // GEOM_ID
     701             : 
     702           0 :     if (nGType == 1)
     703             :     {
     704           0 :         if (14 + 1 + 2 * static_cast<GIntBig>(GetXYLen()) + nZWidth - 1 >
     705             :             INT_MAX)
     706             :         {
     707           0 :             return nullptr;
     708             :         }
     709             :         const double dfX =
     710           0 :             atoi(poRecord->GetField(14, 14 + GetXYLen() - 1)) * GetXYMult() +
     711           0 :             GetXOrigin();
     712             :         const double dfY =
     713           0 :             atoi(poRecord->GetField(14 + GetXYLen(), 14 + GetXYLen() * 2 - 1)) *
     714           0 :                 GetXYMult() +
     715           0 :             GetYOrigin();
     716             :         const double dfZ =
     717           0 :             atoi(poRecord->GetField(14 + 1 + 2 * GetXYLen(),
     718           0 :                                     14 + 1 + 2 * GetXYLen() + nZWidth - 1)) *
     719           0 :             dfZMult;
     720             : 
     721           0 :         poGeometry = new OGRPoint(dfX, dfY, dfZ);
     722             :     }
     723             : 
     724           0 :     else if (nGType == 2)
     725             :     {
     726           0 :         if (nNumCoord < 0 || 14 +
     727           0 :                                      static_cast<GIntBig>(nNumCoord - 1) *
     728           0 :                                          (GetXYLen() * 2 + nZWidth + 2) +
     729           0 :                                      1 + 2 * GetXYLen() + nZWidth - 1 >
     730             :                                  INT_MAX)
     731             :         {
     732           0 :             return nullptr;
     733             :         }
     734             : 
     735           0 :         OGRLineString *poLine = new OGRLineString;
     736           0 :         double dfXLast = 0.0;
     737           0 :         double dfYLast = 0.0;
     738           0 :         int nOutCount = 0;
     739             : 
     740           0 :         poGeometry = poLine;
     741           0 :         poLine->setNumPoints(nNumCoord);
     742           0 :         const GUInt32 nErrorsBefore = CPLGetErrorCounter();
     743           0 :         for (int iCoord = 0; iCoord < nNumCoord; iCoord++)
     744             :         {
     745           0 :             const int iStart = 14 + iCoord * (GetXYLen() * 2 + nZWidth + 2);
     746             : 
     747             :             const char *pszX =
     748           0 :                 poRecord->GetField(iStart + 0, iStart + GetXYLen() - 1);
     749           0 :             bool bSpace = pszX[0] == ' ';
     750           0 :             const double dfX = atoi(pszX) * GetXYMult() + GetXOrigin();
     751           0 :             const char *pszY = poRecord->GetField(iStart + GetXYLen(),
     752           0 :                                                   iStart + GetXYLen() * 2 - 1);
     753           0 :             bSpace |= pszY[0] == ' ';
     754           0 :             const double dfY = atoi(pszY) * GetXYMult() + GetYOrigin();
     755             : 
     756             :             const char *pszZ =
     757           0 :                 poRecord->GetField(iStart + 1 + 2 * GetXYLen(),
     758           0 :                                    iStart + 1 + 2 * GetXYLen() + nZWidth - 1);
     759           0 :             bSpace |= pszZ[0] == ' ';
     760           0 :             const double dfZ = atoi(pszZ) * dfZMult;
     761           0 :             if (bSpace && CPLGetErrorCounter() != nErrorsBefore)
     762             :             {
     763           0 :                 delete poGeometry;
     764           0 :                 return nullptr;
     765             :             }
     766             : 
     767           0 :             if (iCoord == 0)
     768             :             {
     769           0 :                 dfXLast = dfX;
     770           0 :                 dfYLast = dfY;
     771           0 :                 poLine->setPoint(nOutCount++, dfX, dfY, dfZ);
     772             :             }
     773           0 :             else if (dfXLast != dfX || dfYLast != dfY)
     774             :             {
     775           0 :                 dfXLast = dfX;
     776           0 :                 dfYLast = dfY;
     777           0 :                 poLine->setPoint(nOutCount++, dfX, dfY, dfZ);
     778             :             }
     779             :         }
     780           0 :         poLine->setNumPoints(nOutCount);
     781             : 
     782           0 :         CacheAddByGeomId(atoi(poRecord->GetField(3, 8)), poLine);
     783             :     }
     784             : 
     785           0 :     if (poGeometry != nullptr)
     786           0 :         poGeometry->assignSpatialReference(poDS->DSGetSpatialRef());
     787             : 
     788           0 :     return poGeometry;
     789             : }
     790             : 
     791             : /************************************************************************/
     792             : /*                           ProcessAttDesc()                           */
     793             : /************************************************************************/
     794             : 
     795           0 : int NTFFileReader::ProcessAttDesc(NTFRecord *poRecord, NTFAttDesc *psAD)
     796             : 
     797             : {
     798           0 :     psAD->poCodeList = nullptr;
     799           0 :     if (poRecord->GetType() != NRT_ADR || poRecord->GetLength() < 13)
     800           0 :         return FALSE;
     801             : 
     802           0 :     snprintf(psAD->val_type, sizeof(psAD->val_type), "%s",
     803             :              poRecord->GetField(3, 4));
     804           0 :     snprintf(psAD->fwidth, sizeof(psAD->fwidth), "%s",
     805             :              poRecord->GetField(5, 7));
     806           0 :     snprintf(psAD->finter, sizeof(psAD->finter), "%s",
     807             :              poRecord->GetField(8, 12));
     808             : 
     809           0 :     const char *pszData = poRecord->GetData();
     810           0 :     int iChar = 12;  // Used after for.
     811           0 :     for (; pszData[iChar] != '\0' && pszData[iChar] != '\\'; iChar++)
     812             :     {
     813             :     }
     814             : 
     815           0 :     snprintf(psAD->att_name, sizeof(psAD->att_name), "%s",
     816             :              poRecord->GetField(13, iChar));
     817             : 
     818           0 :     return TRUE;
     819             : }
     820             : 
     821             : /************************************************************************/
     822             : /*                         ProcessAttRecGroup()                         */
     823             : /*                                                                      */
     824             : /*      Extract attribute values from all attribute records in a        */
     825             : /*      record set.                                                     */
     826             : /************************************************************************/
     827             : 
     828           0 : int NTFFileReader::ProcessAttRecGroup(NTFRecord **papoRecords,
     829             :                                       char ***ppapszTypes, char ***ppapszValues)
     830             : 
     831             : {
     832           0 :     *ppapszTypes = nullptr;
     833           0 :     *ppapszValues = nullptr;
     834             : 
     835           0 :     for (int iRec = 0; papoRecords[iRec] != nullptr; iRec++)
     836             :     {
     837           0 :         if (papoRecords[iRec]->GetType() != NRT_ATTREC)
     838           0 :             continue;
     839             : 
     840           0 :         char **papszTypes1 = nullptr;
     841           0 :         char **papszValues1 = nullptr;
     842           0 :         if (!ProcessAttRec(papoRecords[iRec], nullptr, &papszTypes1,
     843             :                            &papszValues1))
     844             :         {
     845           0 :             CSLDestroy(*ppapszTypes);
     846           0 :             CSLDestroy(*ppapszValues);
     847           0 :             *ppapszTypes = nullptr;
     848           0 :             *ppapszValues = nullptr;
     849           0 :             return FALSE;
     850             :         }
     851             : 
     852           0 :         if (*ppapszTypes == nullptr)
     853             :         {
     854           0 :             *ppapszTypes = papszTypes1;
     855           0 :             *ppapszValues = papszValues1;
     856             :         }
     857             :         else
     858             :         {
     859           0 :             for (int i = 0; papszTypes1[i] != nullptr; i++)
     860             :             {
     861           0 :                 *ppapszTypes = CSLAddString(*ppapszTypes, papszTypes1[i]);
     862           0 :                 *ppapszValues = CSLAddString(*ppapszValues, papszValues1[i]);
     863             :             }
     864           0 :             CSLDestroy(papszTypes1);
     865           0 :             CSLDestroy(papszValues1);
     866             :         }
     867             :     }
     868             : 
     869           0 :     return TRUE;
     870             : }
     871             : 
     872             : /************************************************************************/
     873             : /*                           ProcessAttRec()                            */
     874             : /************************************************************************/
     875             : 
     876           0 : int NTFFileReader::ProcessAttRec(NTFRecord *poRecord, int *pnAttId,
     877             :                                  char ***ppapszTypes, char ***ppapszValues)
     878             : 
     879             : {
     880           0 :     if (pnAttId != nullptr)
     881           0 :         *pnAttId = 0;
     882           0 :     *ppapszTypes = nullptr;
     883           0 :     *ppapszValues = nullptr;
     884             : 
     885           0 :     if (poRecord->GetType() != NRT_ATTREC || poRecord->GetLength() < 8)
     886           0 :         return FALSE;
     887             : 
     888             :     /* -------------------------------------------------------------------- */
     889             :     /*      Extract the attribute id.                                       */
     890             :     /* -------------------------------------------------------------------- */
     891           0 :     if (pnAttId != nullptr)
     892           0 :         *pnAttId = atoi(poRecord->GetField(3, 8));
     893             : 
     894             :     /* ==================================================================== */
     895             :     /*      Loop handling attribute till we get a '0' indicating the end    */
     896             :     /*      of the record.                                                  */
     897             :     /* ==================================================================== */
     898             : 
     899           0 :     int iOffset = 8;
     900           0 :     const char *pszData = poRecord->GetData();
     901           0 :     bool bError = false;
     902             : 
     903           0 :     while (iOffset < poRecord->GetLength() && pszData[iOffset] != DIGIT_ZERO)
     904             :     {
     905             :         /* --------------------------------------------------------------------
     906             :          */
     907             :         /*      Extract the two letter code name for the attribute, and use */
     908             :         /*      it to find the correct ATTDESC info. */
     909             :         /* --------------------------------------------------------------------
     910             :          */
     911           0 :         NTFAttDesc *psAttDesc = GetAttDesc(pszData + iOffset);
     912           0 :         if (psAttDesc == nullptr)
     913             :         {
     914           0 :             CPLDebug("NTF", "Couldn't translate attrec type `%2.2s'.",
     915           0 :                      pszData + iOffset);
     916           0 :             bError = true;
     917           0 :             break;
     918             :         }
     919             : 
     920           0 :         *ppapszTypes = CSLAddString(
     921             :             *ppapszTypes, poRecord->GetField(iOffset + 1, iOffset + 2));
     922             : 
     923             :         /* --------------------------------------------------------------------
     924             :          */
     925             :         /*      Establish the width of the value.  Zero width fields are */
     926             :         /*      terminated by a backslash. */
     927             :         /* --------------------------------------------------------------------
     928             :          */
     929           0 :         const int nFWidth = atoi(psAttDesc->fwidth);
     930           0 :         if (nFWidth < 0)
     931             :         {
     932           0 :             bError = true;
     933           0 :             break;
     934             :         }
     935           0 :         int nEnd = 0;
     936           0 :         if (nFWidth == 0)
     937             :         {
     938           0 :             const char *pszData2 = poRecord->GetData();
     939           0 :             if (iOffset + 2 >= poRecord->GetLength())
     940             :             {
     941           0 :                 bError = true;
     942           0 :                 break;
     943             :             }
     944           0 :             for (nEnd = iOffset + 2;
     945           0 :                  pszData2[nEnd] != '\\' && pszData2[nEnd] != '\0'; nEnd++)
     946             :             {
     947             :             }
     948             :         }
     949             :         else
     950             :         {
     951           0 :             nEnd = iOffset + 3 + nFWidth - 1;
     952             :         }
     953             : 
     954             :         /* --------------------------------------------------------------------
     955             :          */
     956             :         /*      Extract the value.  If it is formatted as fixed point real */
     957             :         /*      we reprocess it to insert the decimal point. */
     958             :         /* --------------------------------------------------------------------
     959             :          */
     960           0 :         const char *pszRawValue = poRecord->GetField(iOffset + 3, nEnd);
     961           0 :         *ppapszValues = CSLAddString(*ppapszValues, pszRawValue);
     962             : 
     963             :         /* --------------------------------------------------------------------
     964             :          */
     965             :         /*      Establish new offset position. */
     966             :         /* --------------------------------------------------------------------
     967             :          */
     968           0 :         if (nFWidth == 0)
     969             :         {
     970           0 :             iOffset = nEnd;
     971           0 :             if (iOffset >= poRecord->GetLength())
     972             :             {
     973           0 :                 bError = (iOffset > poRecord->GetLength());
     974           0 :                 break;
     975             :             }
     976           0 :             if (pszData[iOffset] == '\\')
     977           0 :                 iOffset++;
     978             :         }
     979             :         else
     980           0 :             iOffset += 2 + nFWidth;
     981             :     }
     982             : 
     983           0 :     if (bError)
     984             :     {
     985           0 :         CSLDestroy(*ppapszTypes);
     986           0 :         CSLDestroy(*ppapszValues);
     987           0 :         *ppapszTypes = nullptr;
     988           0 :         *ppapszValues = nullptr;
     989             :     }
     990             : 
     991           0 :     return (*ppapszTypes != nullptr);
     992             : }
     993             : 
     994             : /************************************************************************/
     995             : /*                             GetAttDesc()                             */
     996             : /************************************************************************/
     997             : 
     998           0 : NTFAttDesc *NTFFileReader::GetAttDesc(const char *pszType)
     999             : 
    1000             : {
    1001           0 :     for (int i = 0; i < nAttCount; i++)
    1002             :     {
    1003           0 :         if (EQUALN(pszType, pasAttDesc[i].val_type, 2))
    1004           0 :             return pasAttDesc + i;
    1005             :     }
    1006             : 
    1007           0 :     return nullptr;
    1008             : }
    1009             : 
    1010             : /************************************************************************/
    1011             : /*                          ProcessAttValue()                           */
    1012             : /*                                                                      */
    1013             : /*      Take an attribute type/value pair and transform into a          */
    1014             : /*      meaningful attribute name, and value.  The source can be an     */
    1015             : /*      ATTREC or the VAL_TYPE/VALUE pair of a POINTREC or LINEREC.     */
    1016             : /*      The name is transformed from the two character short form to    */
    1017             : /*      the long user name.  The value will be transformed from         */
    1018             : /*      fixed point (with the decimal implicit) to fixed point with     */
    1019             : /*      an explicit decimal point if it has a "R" format.               */
    1020             : /*      Note: the returned *ppszAttValue has a very short lifetime      */
    1021             : /*      and should immediately be used. Further calls to                */
    1022             : /*      ProcessAttValue or CPLSPrintf() will invalidate it.             */
    1023             : /************************************************************************/
    1024             : 
    1025           0 : int NTFFileReader::ProcessAttValue(const char *pszValType,
    1026             :                                    const char *pszRawValue,
    1027             :                                    const char **ppszAttName,
    1028             :                                    const char **ppszAttValue,
    1029             :                                    const char **ppszCodeDesc)
    1030             : 
    1031             : {
    1032             :     /* -------------------------------------------------------------------- */
    1033             :     /*      Find the ATTDESC for this attribute, and assign return name value.*/
    1034             :     /* -------------------------------------------------------------------- */
    1035           0 :     NTFAttDesc *psAttDesc = GetAttDesc(pszValType);
    1036             : 
    1037           0 :     if (psAttDesc == nullptr)
    1038           0 :         return FALSE;
    1039             : 
    1040           0 :     if (ppszAttName != nullptr)
    1041           0 :         *ppszAttName = psAttDesc->att_name;
    1042             : 
    1043             :     /* -------------------------------------------------------------------- */
    1044             :     /*      Extract the value.  If it is formatted as fixed point real      */
    1045             :     /*      we reprocess it to insert the decimal point.                    */
    1046             :     /* -------------------------------------------------------------------- */
    1047           0 :     if (psAttDesc->finter[0] == 'R')
    1048             :     {
    1049           0 :         const char *pszDecimalPortion = nullptr;  // Used after for.
    1050             : 
    1051           0 :         for (pszDecimalPortion = psAttDesc->finter;
    1052           0 :              *pszDecimalPortion != ',' && *pszDecimalPortion != '\0';
    1053             :              pszDecimalPortion++)
    1054             :         {
    1055             :         }
    1056           0 :         if (*pszDecimalPortion == '\0')
    1057             :         {
    1058           0 :             *ppszAttValue = "";
    1059             :         }
    1060             :         else
    1061             :         {
    1062           0 :             const int nWidth = static_cast<int>(strlen(pszRawValue));
    1063           0 :             const int nPrecision = atoi(pszDecimalPortion + 1);
    1064           0 :             if (nPrecision < 0 || nPrecision >= nWidth)
    1065             :             {
    1066           0 :                 *ppszAttValue = "";
    1067             :             }
    1068             :             else
    1069             :             {
    1070           0 :                 CPLString osResult(pszRawValue);
    1071           0 :                 osResult.resize(nWidth - nPrecision);
    1072           0 :                 osResult += ".";
    1073           0 :                 osResult += pszRawValue + nWidth - nPrecision;
    1074             : 
    1075           0 :                 *ppszAttValue = CPLSPrintf("%s", osResult.c_str());
    1076             :             }
    1077             :         }
    1078             :     }
    1079             : 
    1080             :     /* -------------------------------------------------------------------- */
    1081             :     /*      If it is an integer, we just reformat to get rid of leading     */
    1082             :     /*      zeros.                                                          */
    1083             :     /* -------------------------------------------------------------------- */
    1084           0 :     else if (psAttDesc->finter[0] == 'I')
    1085             :     {
    1086           0 :         *ppszAttValue = CPLSPrintf("%d", atoi(pszRawValue));
    1087             :     }
    1088             : 
    1089             :     /* -------------------------------------------------------------------- */
    1090             :     /*      Otherwise we take the value directly.                           */
    1091             :     /* -------------------------------------------------------------------- */
    1092             :     else
    1093             :     {
    1094           0 :         *ppszAttValue = pszRawValue;
    1095             :     }
    1096             : 
    1097             :     /* -------------------------------------------------------------------- */
    1098             :     /*      Handle processing code values into code descriptions, if        */
    1099             :     /*      applicable.                                                     */
    1100             :     /* -------------------------------------------------------------------- */
    1101           0 :     if (ppszCodeDesc == nullptr)
    1102             :     {
    1103             :     }
    1104           0 :     else if (psAttDesc->poCodeList != nullptr)
    1105             :     {
    1106           0 :         *ppszCodeDesc = psAttDesc->poCodeList->Lookup(*ppszAttValue);
    1107             :     }
    1108             :     else
    1109             :     {
    1110           0 :         *ppszCodeDesc = nullptr;
    1111             :     }
    1112             : 
    1113           0 :     return TRUE;
    1114             : }
    1115             : 
    1116             : /************************************************************************/
    1117             : /*                        ApplyAttributeValues()                        */
    1118             : /*                                                                      */
    1119             : /*      Apply a series of attribute values to a feature from generic    */
    1120             : /*      attribute records.                                              */
    1121             : /************************************************************************/
    1122             : 
    1123           0 : void NTFFileReader::ApplyAttributeValues(OGRFeature *poFeature,
    1124             :                                          NTFRecord **papoGroup, ...)
    1125             : 
    1126             : {
    1127             :     /* -------------------------------------------------------------------- */
    1128             :     /*      Extract attribute values from record group.                     */
    1129             :     /* -------------------------------------------------------------------- */
    1130           0 :     char **papszTypes = nullptr;
    1131           0 :     char **papszValues = nullptr;
    1132             : 
    1133           0 :     if (!ProcessAttRecGroup(papoGroup, &papszTypes, &papszValues))
    1134           0 :         return;
    1135             : 
    1136             :     /* -------------------------------------------------------------------- */
    1137             :     /*      Handle attribute pairs                                          */
    1138             :     /* -------------------------------------------------------------------- */
    1139             :     va_list hVaArgs;
    1140             : 
    1141           0 :     va_start(hVaArgs, papoGroup);
    1142             : 
    1143           0 :     const char *pszAttName = nullptr;
    1144           0 :     while ((pszAttName = va_arg(hVaArgs, const char *)) != nullptr)
    1145             :     {
    1146           0 :         const int iField = va_arg(hVaArgs, int);
    1147             : 
    1148           0 :         ApplyAttributeValue(poFeature, iField, pszAttName, papszTypes,
    1149             :                             papszValues);
    1150             :     }
    1151             : 
    1152           0 :     va_end(hVaArgs);
    1153             : 
    1154             :     /* -------------------------------------------------------------------- */
    1155             :     /*      Cleanup.                                                        */
    1156             :     /* -------------------------------------------------------------------- */
    1157           0 :     CSLDestroy(papszTypes);
    1158           0 :     CSLDestroy(papszValues);
    1159             : }
    1160             : 
    1161             : /************************************************************************/
    1162             : /*                        ApplyAttributeValue()                         */
    1163             : /*                                                                      */
    1164             : /*      Apply the indicated attribute value to an OGRFeature field      */
    1165             : /*      if it exists in the attribute value list given.                 */
    1166             : /************************************************************************/
    1167             : 
    1168           0 : int NTFFileReader::ApplyAttributeValue(OGRFeature *poFeature, int iField,
    1169             :                                        const char *pszAttName,
    1170             :                                        char **papszTypes, char **papszValues)
    1171             : 
    1172             : {
    1173             :     /* -------------------------------------------------------------------- */
    1174             :     /*      Find the requested attribute in the name/value pair             */
    1175             :     /*      provided.  If not found that's fine, just return with           */
    1176             :     /*      notification.                                                   */
    1177             :     /* -------------------------------------------------------------------- */
    1178           0 :     const int iValue = CSLFindString(papszTypes, pszAttName);
    1179           0 :     if (iValue < 0)
    1180           0 :         return FALSE;
    1181             : 
    1182           0 :     CPLAssert(papszValues != nullptr);
    1183             :     /* -------------------------------------------------------------------- */
    1184             :     /*      Process the attribute value ... this really only has a          */
    1185             :     /*      useful effect for real numbers.                                 */
    1186             :     /* -------------------------------------------------------------------- */
    1187           0 :     const char *pszAttLongName = nullptr;
    1188           0 :     const char *pszAttValue = nullptr;
    1189           0 :     const char *pszCodeDesc = nullptr;
    1190             : 
    1191           0 :     if (!ProcessAttValue(pszAttName, papszValues[iValue], &pszAttLongName,
    1192             :                          &pszAttValue, &pszCodeDesc))
    1193           0 :         return FALSE;
    1194             : 
    1195             :     /* -------------------------------------------------------------------- */
    1196             :     /*      Apply the value to the field using the simple set string        */
    1197             :     /*      method.  Leave it to the OGRFeature::SetField() method to       */
    1198             :     /*      take care of translation to other types.                        */
    1199             :     /* -------------------------------------------------------------------- */
    1200           0 :     poFeature->SetField(iField, pszAttValue);
    1201             : 
    1202             :     /* -------------------------------------------------------------------- */
    1203             :     /*      Apply the code description if we found one.                     */
    1204             :     /* -------------------------------------------------------------------- */
    1205           0 :     if (pszCodeDesc != nullptr)
    1206             :     {
    1207             :         char szDescFieldName[256];
    1208             : 
    1209           0 :         snprintf(szDescFieldName, sizeof(szDescFieldName), "%s_DESC",
    1210           0 :                  poFeature->GetDefnRef()->GetFieldDefn(iField)->GetNameRef());
    1211           0 :         poFeature->SetField(szDescFieldName, pszCodeDesc);
    1212             :     }
    1213             : 
    1214           0 :     return TRUE;
    1215             : }
    1216             : 
    1217             : /************************************************************************/
    1218             : /*                             SaveRecord()                             */
    1219             : /************************************************************************/
    1220             : 
    1221           0 : void NTFFileReader::SaveRecord(NTFRecord *poRecord)
    1222             : 
    1223             : {
    1224           0 :     CPLAssert(poSavedRecord == nullptr);
    1225           0 :     poSavedRecord = poRecord;
    1226           0 : }
    1227             : 
    1228             : /************************************************************************/
    1229             : /*                             ReadRecord()                             */
    1230             : /************************************************************************/
    1231             : 
    1232           0 : NTFRecord *NTFFileReader::ReadRecord()
    1233             : 
    1234             : {
    1235           0 :     if (poSavedRecord != nullptr)
    1236             :     {
    1237           0 :         NTFRecord *poReturn = poSavedRecord;
    1238             : 
    1239           0 :         poSavedRecord = nullptr;
    1240             : 
    1241           0 :         return poReturn;
    1242             :     }
    1243             :     else
    1244             :     {
    1245           0 :         CPLErrorReset();
    1246           0 :         if (fp != nullptr)
    1247           0 :             nPreSavedPos = VSIFTellL(fp);
    1248           0 :         NTFRecord *poRecord = new NTFRecord(fp);
    1249           0 :         if (fp != nullptr)
    1250           0 :             nPostSavedPos = VSIFTellL(fp);
    1251             : 
    1252             :         /* ensure termination if we fail to read a record */
    1253           0 :         if (CPLGetLastErrorType() == CE_Failure)
    1254             :         {
    1255           0 :             delete poRecord;
    1256           0 :             poRecord = nullptr;
    1257             :         }
    1258             : 
    1259           0 :         return poRecord;
    1260             :     }
    1261             : }
    1262             : 
    1263             : /************************************************************************/
    1264             : /*                              GetFPPos()                              */
    1265             : /*                                                                      */
    1266             : /*      Return the current file pointer position.                       */
    1267             : /************************************************************************/
    1268             : 
    1269           0 : void NTFFileReader::GetFPPos(vsi_l_offset *pnPos, long *pnFID)
    1270             : 
    1271             : {
    1272           0 :     if (poSavedRecord != nullptr)
    1273           0 :         *pnPos = nPreSavedPos;
    1274             :     else
    1275           0 :         *pnPos = nPostSavedPos;
    1276             : 
    1277           0 :     if (pnFID != nullptr)
    1278           0 :         *pnFID = nSavedFeatureId;
    1279           0 : }
    1280             : 
    1281             : /************************************************************************/
    1282             : /*                              SetFPPos()                              */
    1283             : /************************************************************************/
    1284             : 
    1285           0 : int NTFFileReader::SetFPPos(vsi_l_offset nNewPos, long nNewFID)
    1286             : 
    1287             : {
    1288           0 :     if (nNewFID == nSavedFeatureId)
    1289           0 :         return TRUE;
    1290             : 
    1291           0 :     if (poSavedRecord != nullptr)
    1292             :     {
    1293           0 :         delete poSavedRecord;
    1294           0 :         poSavedRecord = nullptr;
    1295             :     }
    1296             : 
    1297           0 :     if (fp != nullptr && VSIFSeekL(fp, nNewPos, SEEK_SET) == 0)
    1298             :     {
    1299           0 :         nPreSavedPos = nPostSavedPos = nNewPos;
    1300           0 :         nSavedFeatureId = nNewFID;
    1301           0 :         return TRUE;
    1302             :     }
    1303             :     else
    1304           0 :         return FALSE;
    1305             : }
    1306             : 
    1307             : /************************************************************************/
    1308             : /*                               Reset()                                */
    1309             : /*                                                                      */
    1310             : /*      Reset reading to the first feature record.                      */
    1311             : /************************************************************************/
    1312             : 
    1313           0 : void NTFFileReader::Reset()
    1314             : 
    1315             : {
    1316           0 :     SetFPPos(nStartPos, nBaseFeatureId);
    1317           0 :     ClearCGroup();
    1318           0 : }
    1319             : 
    1320             : /************************************************************************/
    1321             : /*                            ClearCGroup()                             */
    1322             : /*                                                                      */
    1323             : /*      Clear the currently loaded record group.                        */
    1324             : /************************************************************************/
    1325             : 
    1326           0 : void NTFFileReader::ClearCGroup()
    1327             : 
    1328             : {
    1329           0 :     for (int i = 0; apoCGroup[i] != nullptr; i++)
    1330           0 :         delete apoCGroup[i];
    1331             : 
    1332           0 :     apoCGroup[0] = nullptr;
    1333           0 :     apoCGroup[1] = nullptr;
    1334           0 : }
    1335             : 
    1336             : /************************************************************************/
    1337             : /*                      DefaultNTFRecordGrouper()                       */
    1338             : /*                                                                      */
    1339             : /*      Default rules for figuring out if a new candidate record        */
    1340             : /*      belongs to a group of records that together form a feature      */
    1341             : /*      (a record group).                                               */
    1342             : /************************************************************************/
    1343             : 
    1344           0 : int DefaultNTFRecordGrouper(NTFFileReader *, NTFRecord **papoGroup,
    1345             :                             NTFRecord *poCandidate)
    1346             : 
    1347             : {
    1348             :     /* -------------------------------------------------------------------- */
    1349             :     /*      Is this group going to be a CPOLY set?  We can recognise        */
    1350             :     /*      this because we get repeating POLY/CHAIN sets without an        */
    1351             :     /*      intermediate attribute record.  This is a rather special case!  */
    1352             :     /* -------------------------------------------------------------------- */
    1353           0 :     if (papoGroup[0] != nullptr && papoGroup[1] != nullptr &&
    1354           0 :         papoGroup[0]->GetType() == NRT_POLYGON &&
    1355           0 :         papoGroup[1]->GetType() == NRT_CHAIN)
    1356             :     {
    1357             :         // We keep going till we get the seed geometry.
    1358           0 :         int iRec, bGotCPOLY = FALSE;
    1359             : 
    1360           0 :         for (iRec = 0; papoGroup[iRec] != nullptr; iRec++)
    1361             :         {
    1362           0 :             if (papoGroup[iRec]->GetType() == NRT_CPOLY)
    1363           0 :                 bGotCPOLY = TRUE;
    1364             :         }
    1365             : 
    1366           0 :         if (bGotCPOLY && poCandidate->GetType() != NRT_GEOMETRY &&
    1367           0 :             poCandidate->GetType() != NRT_ATTREC)
    1368           0 :             return FALSE;
    1369             : 
    1370             :         /*
    1371             :          * this logic assumes we always get a point geometry with a CPOLY
    1372             :          * but that isn't always true, for instance with BL2000 data.  The
    1373             :          * preceding check will handle this case.
    1374             :          */
    1375           0 :         if (papoGroup[iRec - 1]->GetType() != NRT_GEOMETRY)
    1376           0 :             return TRUE;
    1377             :         else
    1378           0 :             return FALSE;
    1379             :     }
    1380             : 
    1381             :     /* -------------------------------------------------------------------- */
    1382             :     /*      Is this a "feature" defining record?  If so break out if it     */
    1383             :     /*      isn't the first record in the group.                            */
    1384             :     /* -------------------------------------------------------------------- */
    1385           0 :     if (papoGroup[0] != nullptr && (poCandidate->GetType() == NRT_NAMEREC ||
    1386           0 :                                     poCandidate->GetType() == NRT_NODEREC ||
    1387           0 :                                     poCandidate->GetType() == NRT_LINEREC ||
    1388           0 :                                     poCandidate->GetType() == NRT_POINTREC ||
    1389           0 :                                     poCandidate->GetType() == NRT_POLYGON ||
    1390           0 :                                     poCandidate->GetType() == NRT_CPOLY ||
    1391           0 :                                     poCandidate->GetType() == NRT_COLLECT ||
    1392           0 :                                     poCandidate->GetType() == NRT_TEXTREC ||
    1393           0 :                                     poCandidate->GetType() == NRT_COMMENT))
    1394             :     {
    1395           0 :         return FALSE;
    1396             :     }
    1397             : 
    1398             :     /* -------------------------------------------------------------------- */
    1399             :     /*      Do we already have a record of this type?  If so, it likely     */
    1400             :     /*      doesn't belong in the group.  Attribute records do repeat in    */
    1401             :     /*      some products.                                                  */
    1402             :     /* -------------------------------------------------------------------- */
    1403           0 :     if (poCandidate->GetType() != NRT_ATTREC)
    1404             :     {
    1405           0 :         int iRec = 0;  // Used after for.
    1406           0 :         for (; papoGroup[iRec] != nullptr; iRec++)
    1407             :         {
    1408           0 :             if (poCandidate->GetType() == papoGroup[iRec]->GetType())
    1409           0 :                 break;
    1410             :         }
    1411             : 
    1412           0 :         if (papoGroup[iRec] != nullptr)
    1413           0 :             return FALSE;
    1414             :     }
    1415             : 
    1416           0 :     return TRUE;
    1417             : }
    1418             : 
    1419             : /************************************************************************/
    1420             : /*                          ReadRecordGroup()                           */
    1421             : /*                                                                      */
    1422             : /*      Read a group of records that form a single feature.             */
    1423             : /************************************************************************/
    1424             : 
    1425           0 : NTFRecord **NTFFileReader::ReadRecordGroup()
    1426             : 
    1427             : {
    1428             : 
    1429           0 :     ClearCGroup();
    1430             : 
    1431             :     /* -------------------------------------------------------------------- */
    1432             :     /*      Loop, reading records till we think we have a grouping.         */
    1433             :     /* -------------------------------------------------------------------- */
    1434           0 :     int nRecordCount = 0;
    1435           0 :     NTFRecord *poRecord = nullptr;
    1436           0 :     while ((poRecord = ReadRecord()) != nullptr &&
    1437           0 :            poRecord->GetType() != NRT_VTR)
    1438             :     {
    1439           0 :         if (nRecordCount >= MAX_REC_GROUP)
    1440             :         {
    1441           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1442             :                      "Maximum record group size (%d) exceeded.\n",
    1443             :                      MAX_REC_GROUP);
    1444           0 :             break;
    1445             :         }
    1446             : 
    1447           0 :         if (!pfnRecordGrouper(this, apoCGroup, poRecord))
    1448           0 :             break;
    1449             : 
    1450           0 :         apoCGroup[nRecordCount++] = poRecord;
    1451           0 :         apoCGroup[nRecordCount] = nullptr;
    1452             :     }
    1453             : 
    1454             :     /* -------------------------------------------------------------------- */
    1455             :     /*      Push the last record back on the input queue.                   */
    1456             :     /* -------------------------------------------------------------------- */
    1457           0 :     if (poRecord != nullptr)
    1458           0 :         SaveRecord(poRecord);
    1459             : 
    1460             :     /* -------------------------------------------------------------------- */
    1461             :     /*      Return the list, or NULL if we didn't get any records.          */
    1462             :     /* -------------------------------------------------------------------- */
    1463           0 :     if (nRecordCount == 0)
    1464           0 :         return nullptr;
    1465             :     else
    1466           0 :         return apoCGroup;
    1467             : }
    1468             : 
    1469             : /************************************************************************/
    1470             : /*                          GetFeatureClass()                           */
    1471             : /************************************************************************/
    1472             : 
    1473           0 : int NTFFileReader::GetFeatureClass(int iFCIndex, char **ppszFCId,
    1474             :                                    char **ppszFCName)
    1475             : 
    1476             : {
    1477           0 :     if (iFCIndex < 0 || iFCIndex >= nFCCount)
    1478             :     {
    1479           0 :         *ppszFCId = nullptr;
    1480           0 :         *ppszFCName = nullptr;
    1481           0 :         return FALSE;
    1482             :     }
    1483             :     else
    1484             :     {
    1485           0 :         *ppszFCId = papszFCNum[iFCIndex];
    1486           0 :         *ppszFCName = papszFCName[iFCIndex];
    1487           0 :         return TRUE;
    1488             :     }
    1489             : }
    1490             : 
    1491             : /************************************************************************/
    1492             : /*                           ReadOGRFeature()                           */
    1493             : /************************************************************************/
    1494             : 
    1495           0 : OGRFeature *NTFFileReader::ReadOGRFeature(OGRNTFLayer *poTargetLayer)
    1496             : 
    1497             : {
    1498             :     /* -------------------------------------------------------------------- */
    1499             :     /*      If this is a raster file, use a custom method to read the       */
    1500             :     /*      feature.                                                        */
    1501             :     /* -------------------------------------------------------------------- */
    1502           0 :     if (IsRasterProduct())
    1503           0 :         return poRasterLayer->GetNextFeature();
    1504             : 
    1505             :     /* -------------------------------------------------------------------- */
    1506             :     /*      Loop looking for a group we can translate, and that if          */
    1507             :     /*      needed matches our layer request.                               */
    1508             :     /* -------------------------------------------------------------------- */
    1509           0 :     OGRNTFLayer *poLayer = nullptr;
    1510           0 :     OGRFeature *poFeature = nullptr;
    1511             : 
    1512             :     while (true)
    1513             :     {
    1514           0 :         NTFRecord **papoGroup = nullptr;
    1515             : 
    1516           0 :         if (GetProductId() == NPC_UNKNOWN && nNTFLevel > 2)
    1517           0 :             papoGroup = GetNextIndexedRecordGroup(apoCGroup + 1);
    1518             :         else
    1519           0 :             papoGroup = ReadRecordGroup();
    1520             : 
    1521           0 :         if (papoGroup == nullptr || papoGroup[0] == nullptr)
    1522             :             break;
    1523             : 
    1524           0 :         int nType = papoGroup[0]->GetType();
    1525           0 :         if (nType < 0 || nType >= (int)(sizeof(apoTypeTranslation) /
    1526             :                                         sizeof(apoTypeTranslation[0])))
    1527           0 :             continue;
    1528           0 :         poLayer = apoTypeTranslation[nType];
    1529           0 :         if (poLayer == nullptr)
    1530           0 :             continue;
    1531             : 
    1532           0 :         if (poTargetLayer != nullptr && poTargetLayer != poLayer)
    1533             :         {
    1534           0 :             CacheLineGeometryInGroup(papoGroup);
    1535           0 :             nSavedFeatureId++;
    1536           0 :             continue;
    1537             :         }
    1538             : 
    1539           0 :         poFeature = poLayer->FeatureTranslate(this, papoGroup);
    1540           0 :         if (poFeature == nullptr)
    1541             :         {
    1542             :             // should this be a real error?
    1543           0 :             CPLDebug("NTF",
    1544             :                      "FeatureTranslate() failed for a type %d record group\n"
    1545             :                      "in a %s type file.\n",
    1546             :                      papoGroup[0]->GetType(), GetProduct());
    1547             :         }
    1548             :         else
    1549             :         {
    1550           0 :             break;
    1551             :         }
    1552           0 :     }
    1553             : 
    1554             :     /* -------------------------------------------------------------------- */
    1555             :     /*      If we got a feature, set the TILE_REF on it.                    */
    1556             :     /* -------------------------------------------------------------------- */
    1557           0 :     if (poFeature != nullptr)
    1558             :     {
    1559           0 :         int iTileRefField = poLayer->GetLayerDefn()->GetFieldCount() - 1;
    1560             : 
    1561           0 :         CPLAssert(EQUAL(
    1562             :             poLayer->GetLayerDefn()->GetFieldDefn(iTileRefField)->GetNameRef(),
    1563             :             "TILE_REF"));
    1564             : 
    1565           0 :         poFeature->SetField(iTileRefField, GetTileName());
    1566           0 :         poFeature->SetFID(nSavedFeatureId);
    1567             : 
    1568           0 :         nSavedFeatureId++;
    1569             :     }
    1570             : 
    1571             :     /* -------------------------------------------------------------------- */
    1572             :     /*      If we got to the end we can establish our feature count for     */
    1573             :     /*      the file.                                                       */
    1574             :     /* -------------------------------------------------------------------- */
    1575             :     else
    1576             :     {
    1577             :         // This assertion was triggered by
    1578             :         // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=1982 it doesn't
    1579             :         // look critical enough to be enabled.
    1580             :         // CPLAssert( nFeatureCount == -1
    1581             :         //           || nFeatureCount == nSavedFeatureId - nBaseFeatureId );
    1582           0 :         nFeatureCount = nSavedFeatureId - nBaseFeatureId;
    1583             :     }
    1584             : 
    1585           0 :     return poFeature;
    1586             : }
    1587             : 
    1588             : /************************************************************************/
    1589             : /*                            TestForLayer()                            */
    1590             : /*                                                                      */
    1591             : /*      Return indicator of whether this file contains any features     */
    1592             : /*      of the indicated layer type.                                    */
    1593             : /************************************************************************/
    1594             : 
    1595           0 : int NTFFileReader::TestForLayer(OGRNTFLayer *poLayer)
    1596             : 
    1597             : {
    1598           0 :     for (int i = 0; i < 100; i++)
    1599             :     {
    1600           0 :         if (apoTypeTranslation[i] == poLayer)
    1601           0 :             return TRUE;
    1602             :     }
    1603             : 
    1604           0 :     return FALSE;
    1605             : }
    1606             : 
    1607             : /************************************************************************/
    1608             : /*                            FreshenIndex()                            */
    1609             : /*                                                                      */
    1610             : /*      Rebuild the index if it is needed, and currently missing.       */
    1611             : /************************************************************************/
    1612             : 
    1613           0 : void NTFFileReader::FreshenIndex()
    1614             : 
    1615             : {
    1616           0 :     if (!bIndexBuilt && bIndexNeeded)
    1617           0 :         IndexFile();
    1618           0 : }
    1619             : 
    1620             : /************************************************************************/
    1621             : /*                             IndexFile()                              */
    1622             : /*                                                                      */
    1623             : /*      Read all records beyond the section header and build an         */
    1624             : /*      internal index of them.                                         */
    1625             : /************************************************************************/
    1626             : 
    1627           0 : void NTFFileReader::IndexFile()
    1628             : 
    1629             : {
    1630           0 :     Reset();
    1631             : 
    1632           0 :     DestroyIndex();
    1633             : 
    1634           0 :     bIndexNeeded = TRUE;
    1635           0 :     bIndexBuilt = TRUE;
    1636           0 :     bCacheLines = FALSE;
    1637             : 
    1638             :     /* -------------------------------------------------------------------- */
    1639             :     /*      Process all records after the section header, and before 99     */
    1640             :     /*      to put them in the index.                                       */
    1641             :     /* -------------------------------------------------------------------- */
    1642           0 :     NTFRecord *poRecord = nullptr;
    1643           0 :     while ((poRecord = ReadRecord()) != nullptr && poRecord->GetType() != 99)
    1644             :     {
    1645           0 :         const int iType = poRecord->GetType();
    1646           0 :         const int iId = atoi(poRecord->GetField(3, 8));
    1647             : 
    1648           0 :         if (iType < 0 || iType >= 100)
    1649             :         {
    1650           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1651             :                      "Illegal type %d record, skipping.", iType);
    1652           0 :             delete poRecord;
    1653           0 :             continue;
    1654             :         }
    1655           0 :         if (iId < 0)
    1656             :         {
    1657           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1658             :                      "Illegal id %d record, skipping.", iId);
    1659           0 :             delete poRecord;
    1660           0 :             continue;
    1661             :         }
    1662             : 
    1663             :         /* --------------------------------------------------------------------
    1664             :          */
    1665             :         /*      Grow type specific subindex if needed. */
    1666             :         /* --------------------------------------------------------------------
    1667             :          */
    1668           0 :         if (anIndexSize[iType] <= iId)
    1669             :         {
    1670           0 :             const int nNewSize = std::max(iId + 1, anIndexSize[iType] * 2 + 10);
    1671             : 
    1672           0 :             apapoRecordIndex[iType] = static_cast<NTFRecord **>(
    1673           0 :                 CPLRealloc(apapoRecordIndex[iType], sizeof(void *) * nNewSize));
    1674             : 
    1675           0 :             for (int i = anIndexSize[iType]; i < nNewSize; i++)
    1676           0 :                 (apapoRecordIndex[iType])[i] = nullptr;
    1677             : 
    1678           0 :             anIndexSize[iType] = nNewSize;
    1679             :         }
    1680             : 
    1681             :         /* --------------------------------------------------------------------
    1682             :          */
    1683             :         /*      Put record into type specific subindex based on its id as */
    1684             :         /*      the key. */
    1685             :         /* --------------------------------------------------------------------
    1686             :          */
    1687           0 :         if (apapoRecordIndex[iType][iId] != nullptr)
    1688             :         {
    1689           0 :             CPLDebug("OGR_NTF",
    1690             :                      "Duplicate record with index %d and type %d\n"
    1691             :                      "in NTFFileReader::IndexFile().",
    1692             :                      iId, iType);
    1693           0 :             delete apapoRecordIndex[iType][iId];
    1694             :         }
    1695           0 :         (apapoRecordIndex[iType])[iId] = poRecord;
    1696             :     }
    1697             : 
    1698           0 :     if (poRecord != nullptr)
    1699           0 :         delete poRecord;
    1700           0 : }
    1701             : 
    1702             : /************************************************************************/
    1703             : /*                            DestroyIndex()                            */
    1704             : /************************************************************************/
    1705             : 
    1706           0 : void NTFFileReader::DestroyIndex()
    1707             : 
    1708             : {
    1709           0 :     for (int i = 0; i < 100; i++)
    1710             :     {
    1711           0 :         for (int iId = 0; iId < anIndexSize[i]; iId++)
    1712             :         {
    1713           0 :             if ((apapoRecordIndex[i])[iId] != nullptr)
    1714           0 :                 delete (apapoRecordIndex[i])[iId];
    1715             :         }
    1716             : 
    1717           0 :         CPLFree(apapoRecordIndex[i]);
    1718           0 :         apapoRecordIndex[i] = nullptr;
    1719           0 :         anIndexSize[i] = 0;
    1720             :     }
    1721             : 
    1722           0 :     bIndexBuilt = FALSE;
    1723           0 : }
    1724             : 
    1725             : /************************************************************************/
    1726             : /*                          GetIndexedRecord()                          */
    1727             : /************************************************************************/
    1728             : 
    1729           0 : NTFRecord *NTFFileReader::GetIndexedRecord(int iType, int iId)
    1730             : 
    1731             : {
    1732           0 :     if ((iType < 0 || iType > 99) || (iId < 0 || iId >= anIndexSize[iType]) ||
    1733           0 :         (apapoRecordIndex[iType])[iId] == nullptr)
    1734             :     {
    1735             :         /* If NRT_GEOMETRY3D is an acceptable alternative to 2D */
    1736           0 :         if (iType == NRT_GEOMETRY)
    1737           0 :             return GetIndexedRecord(NRT_GEOMETRY3D, iId);
    1738             :         else
    1739           0 :             return nullptr;
    1740             :     }
    1741             : 
    1742           0 :     return (apapoRecordIndex[iType])[iId];
    1743             : }
    1744             : 
    1745             : /************************************************************************/
    1746             : /*                          AddToIndexGroup()                           */
    1747             : /************************************************************************/
    1748             : 
    1749           0 : void NTFFileReader::AddToIndexGroup(NTFRecord *poRecord)
    1750             : 
    1751             : {
    1752           0 :     int i = 1;  // Used after for.
    1753           0 :     for (; apoCGroup[i] != nullptr; i++)
    1754             :     {
    1755           0 :         if (apoCGroup[i] == poRecord)
    1756             :         {
    1757           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1758             :                      "Record already inserted in group");
    1759           0 :             return;
    1760             :         }
    1761             :     }
    1762           0 :     if (i == MAX_REC_GROUP)
    1763             :     {
    1764           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1765             :                  "Maximum number of records in group reached");
    1766           0 :         delete poRecord;
    1767           0 :         return;
    1768             :     }
    1769             : 
    1770           0 :     apoCGroup[i] = poRecord;
    1771           0 :     apoCGroup[i + 1] = nullptr;
    1772             : }
    1773             : 
    1774             : /************************************************************************/
    1775             : /*                     GetNextIndexedRecordGroup()                      */
    1776             : /************************************************************************/
    1777             : 
    1778           0 : NTFRecord **NTFFileReader::GetNextIndexedRecordGroup(NTFRecord **papoPrevGroup)
    1779             : 
    1780             : {
    1781             :     int nPrevType, nPrevId;
    1782             : 
    1783             :     /* -------------------------------------------------------------------- */
    1784             :     /*      What was the identify of our previous anchor record?            */
    1785             :     /* -------------------------------------------------------------------- */
    1786           0 :     if (papoPrevGroup == nullptr || papoPrevGroup[0] == nullptr)
    1787             :     {
    1788           0 :         nPrevType = NRT_POINTREC;
    1789           0 :         nPrevId = 0;
    1790           0 :         FreshenIndex();
    1791             :     }
    1792             :     else
    1793             :     {
    1794           0 :         nPrevType = papoPrevGroup[0]->GetType();
    1795           0 :         nPrevId = atoi(papoPrevGroup[0]->GetField(3, 8));
    1796           0 :         if (nPrevId < 0)
    1797           0 :             return nullptr;
    1798             :     }
    1799             : 
    1800             :     /* -------------------------------------------------------------------- */
    1801             :     /*      Find the next anchor record.                                    */
    1802             :     /* -------------------------------------------------------------------- */
    1803           0 :     NTFRecord *poAnchor = nullptr;
    1804             : 
    1805           0 :     while (nPrevType != 99 && poAnchor == nullptr)
    1806             :     {
    1807           0 :         nPrevId++;
    1808           0 :         if (nPrevId >= anIndexSize[nPrevType])
    1809             :         {
    1810           0 :             do
    1811             :             {
    1812           0 :                 nPrevType++;
    1813           0 :             } while (nPrevType != NRT_VTR && nPrevType != NRT_NODEREC &&
    1814           0 :                      nPrevType != NRT_TEXTREC && nPrevType != NRT_NAMEREC &&
    1815           0 :                      nPrevType != NRT_COLLECT && nPrevType != NRT_POLYGON &&
    1816           0 :                      nPrevType != NRT_CPOLY && nPrevType != NRT_POINTREC &&
    1817             :                      nPrevType != NRT_LINEREC);
    1818             : 
    1819           0 :             nPrevId = 0;
    1820             :         }
    1821             :         else
    1822             :         {
    1823           0 :             poAnchor = (apapoRecordIndex[nPrevType])[nPrevId];
    1824             :         }
    1825             :     }
    1826             : 
    1827           0 :     if (poAnchor == nullptr)
    1828             :     {
    1829           0 :         return nullptr;
    1830             :     }
    1831             : 
    1832             :     /* -------------------------------------------------------------------- */
    1833             :     /*      Build record group depending on type of anchor and what it      */
    1834             :     /*      refers to.                                                      */
    1835             :     /* -------------------------------------------------------------------- */
    1836           0 :     apoCGroup[0] = nullptr;
    1837           0 :     apoCGroup[1] = poAnchor;
    1838           0 :     apoCGroup[2] = nullptr;
    1839             : 
    1840             :     /* -------------------------------------------------------------------- */
    1841             :     /*      Handle POINTREC/LINEREC                                         */
    1842             :     /* -------------------------------------------------------------------- */
    1843           0 :     if (poAnchor->GetType() == NRT_POINTREC ||
    1844           0 :         poAnchor->GetType() == NRT_LINEREC)
    1845             :     {
    1846           0 :         int l_nAttCount = 0;
    1847             : 
    1848           0 :         AddToIndexGroup(
    1849             :             GetIndexedRecord(NRT_GEOMETRY, atoi(poAnchor->GetField(9, 14))));
    1850             : 
    1851           0 :         if (poAnchor->GetLength() >= 16)
    1852           0 :             l_nAttCount = atoi(poAnchor->GetField(15, 16));
    1853             : 
    1854           0 :         for (int iAtt = 0; iAtt < l_nAttCount; iAtt++)
    1855             :         {
    1856           0 :             AddToIndexGroup(GetIndexedRecord(
    1857             :                 NRT_ATTREC,
    1858           0 :                 atoi(poAnchor->GetField(17 + 6 * iAtt, 22 + 6 * iAtt))));
    1859             :         }
    1860             :     }
    1861             : 
    1862             :     /* -------------------------------------------------------------------- */
    1863             :     /*      Handle TEXTREC                                                  */
    1864             :     /* -------------------------------------------------------------------- */
    1865           0 :     else if (poAnchor->GetType() == NRT_TEXTREC)
    1866             :     {
    1867           0 :         int l_nAttCount = 0;
    1868           0 :         int nSelCount = 0;
    1869             : 
    1870             :         // Add all the text position records.
    1871           0 :         nSelCount = atoi(poAnchor->GetField(9, 10));
    1872           0 :         if (nSelCount < 0)
    1873           0 :             return nullptr;
    1874             : 
    1875           0 :         for (int iSel = 0; iSel < nSelCount; iSel++)
    1876             :         {
    1877           0 :             int iStart = 11 + 12 * iSel + 6;
    1878             : 
    1879           0 :             AddToIndexGroup(GetIndexedRecord(
    1880             :                 NRT_TEXTPOS, atoi(poAnchor->GetField(iStart, iStart + 5))));
    1881             :         }
    1882             : 
    1883             :         // Add all geometry and TEXR records pointed to by text position
    1884             :         // records.
    1885           0 :         for (int iRec = 1; apoCGroup[iRec] != nullptr; iRec++)
    1886             :         {
    1887           0 :             NTFRecord *poRecord = apoCGroup[iRec];
    1888             : 
    1889           0 :             if (poRecord->GetType() != NRT_TEXTPOS)
    1890           0 :                 continue;
    1891             : 
    1892           0 :             const int nNumTEXR = atoi(poRecord->GetField(9, 10));
    1893           0 :             for (int iTEXR = 0; iTEXR < nNumTEXR; iTEXR++)
    1894             :             {
    1895           0 :                 AddToIndexGroup(GetIndexedRecord(
    1896           0 :                     NRT_TEXTREP, atoi(poRecord->GetField(11 + iTEXR * 12,
    1897           0 :                                                          16 + iTEXR * 12))));
    1898           0 :                 AddToIndexGroup(GetIndexedRecord(
    1899           0 :                     NRT_GEOMETRY, atoi(poRecord->GetField(17 + iTEXR * 12,
    1900           0 :                                                           22 + iTEXR * 12))));
    1901             :             }
    1902             :         }
    1903             : 
    1904             :         // Add all the attribute records.
    1905           0 :         if (poAnchor->GetLength() >= 10 + nSelCount * 12 + 2)
    1906           0 :             l_nAttCount = atoi(
    1907           0 :                 poAnchor->GetField(11 + nSelCount * 12, 12 + nSelCount * 12));
    1908             : 
    1909           0 :         for (int iAtt = 0; iAtt < l_nAttCount; iAtt++)
    1910             :         {
    1911           0 :             int iStart = 13 + nSelCount * 12 + 6 * iAtt;
    1912             : 
    1913           0 :             AddToIndexGroup(GetIndexedRecord(
    1914             :                 NRT_ATTREC, atoi(poAnchor->GetField(iStart, iStart + 5))));
    1915             :         }
    1916             :     }
    1917             : 
    1918             :     /* -------------------------------------------------------------------- */
    1919             :     /*      Handle NODEREC.                                                 */
    1920             :     /* -------------------------------------------------------------------- */
    1921           0 :     else if (poAnchor->GetType() == NRT_NODEREC)
    1922             :     {
    1923           0 :         AddToIndexGroup(
    1924             :             GetIndexedRecord(NRT_GEOMETRY, atoi(poAnchor->GetField(9, 14))));
    1925             :     }
    1926             : 
    1927             :     /* -------------------------------------------------------------------- */
    1928             :     /*      Handle COLLECT.                                                 */
    1929             :     /* -------------------------------------------------------------------- */
    1930           0 :     else if (poAnchor->GetType() == NRT_COLLECT)
    1931             :     {
    1932           0 :         const int nParts = atoi(poAnchor->GetField(9, 12));
    1933           0 :         if (nParts < 0)
    1934           0 :             return nullptr;
    1935           0 :         const int nAttOffset = 13 + nParts * 8;
    1936           0 :         int l_nAttCount = 0;
    1937             : 
    1938           0 :         if (poAnchor->GetLength() > nAttOffset + 2)
    1939           0 :             l_nAttCount = atoi(poAnchor->GetField(nAttOffset, nAttOffset + 1));
    1940             : 
    1941           0 :         for (int iAtt = 0; iAtt < l_nAttCount; iAtt++)
    1942             :         {
    1943           0 :             const int iStart = nAttOffset + 2 + iAtt * 6;
    1944             : 
    1945           0 :             AddToIndexGroup(GetIndexedRecord(
    1946             :                 NRT_ATTREC, atoi(poAnchor->GetField(iStart, iStart + 5))));
    1947             :         }
    1948             :     }
    1949             : 
    1950             :     /* -------------------------------------------------------------------- */
    1951             :     /*      Handle POLYGON                                                  */
    1952             :     /* -------------------------------------------------------------------- */
    1953           0 :     else if (poAnchor->GetType() == NRT_POLYGON)
    1954             :     {
    1955           0 :         AddToIndexGroup(
    1956             :             GetIndexedRecord(NRT_CHAIN, atoi(poAnchor->GetField(9, 14))));
    1957             : 
    1958           0 :         if (poAnchor->GetLength() >= 20)
    1959           0 :             AddToIndexGroup(GetIndexedRecord(NRT_GEOMETRY,
    1960             :                                              atoi(poAnchor->GetField(15, 20))));
    1961             : 
    1962             :         // Attributes
    1963           0 :         int l_nAttCount = 0;
    1964             : 
    1965           0 :         if (poAnchor->GetLength() >= 22)
    1966           0 :             l_nAttCount = atoi(poAnchor->GetField(21, 22));
    1967             : 
    1968           0 :         for (int iAtt = 0; iAtt < l_nAttCount; iAtt++)
    1969             :         {
    1970           0 :             AddToIndexGroup(GetIndexedRecord(
    1971             :                 NRT_ATTREC,
    1972           0 :                 atoi(poAnchor->GetField(23 + 6 * iAtt, 28 + 6 * iAtt))));
    1973             :         }
    1974             :     }
    1975             :     /* -------------------------------------------------------------------- */
    1976             :     /*      Handle CPOLY                                                    */
    1977             :     /* -------------------------------------------------------------------- */
    1978           0 :     else if (poAnchor->GetType() == NRT_CPOLY)
    1979             :     {
    1980           0 :         int nPolyCount = atoi(poAnchor->GetField(9, 12));
    1981           0 :         if (nPolyCount < 0)
    1982           0 :             return nullptr;
    1983           0 :         int nPostPoly = nPolyCount * 7 + 12;
    1984             : 
    1985           0 :         if (poAnchor->GetLength() >= nPostPoly + 6)
    1986             :         {
    1987             :             int nGeomId =
    1988           0 :                 atoi(poAnchor->GetField(nPostPoly + 1, nPostPoly + 6));
    1989             : 
    1990           0 :             AddToIndexGroup(GetIndexedRecord(NRT_GEOMETRY, nGeomId));
    1991             :         }
    1992             : 
    1993           0 :         if (poAnchor->GetLength() >= nPostPoly + 8)
    1994             :         {
    1995             :             int l_nAttCount =
    1996           0 :                 atoi(poAnchor->GetField(nPostPoly + 7, nPostPoly + 8));
    1997             : 
    1998           0 :             for (int iAtt = 0; iAtt < l_nAttCount; iAtt++)
    1999             :             {
    2000           0 :                 int nAttId = atoi(poAnchor->GetField(
    2001           0 :                     nPostPoly + 9 + iAtt * 6, nPostPoly + 14 + iAtt * 6));
    2002           0 :                 AddToIndexGroup(GetIndexedRecord(NRT_ATTREC, nAttId));
    2003             :             }
    2004             :         }
    2005             :     }
    2006             : 
    2007           0 :     return apoCGroup + 1;
    2008             : }
    2009             : 
    2010             : /************************************************************************/
    2011             : /*                          OverrideTileName()                          */
    2012             : /************************************************************************/
    2013             : 
    2014           0 : void NTFFileReader::OverrideTileName(const char *pszNewName)
    2015             : 
    2016             : {
    2017           0 :     CPLFree(pszTileName);
    2018           0 :     pszTileName = CPLStrdup(pszNewName);
    2019           0 : }
    2020             : 
    2021             : /************************************************************************/
    2022             : /*                          CacheAddByGeomId()                          */
    2023             : /*                                                                      */
    2024             : /*      Add a geometry to the geometry cache given its GEOMID as        */
    2025             : /*      the index.                                                      */
    2026             : /************************************************************************/
    2027             : 
    2028           0 : void NTFFileReader::CacheAddByGeomId(int nGeomId, OGRGeometry *poGeometry)
    2029             : 
    2030             : {
    2031           0 :     if (!bCacheLines)
    2032           0 :         return;
    2033             : 
    2034           0 :     CPLAssert(nGeomId >= 0);
    2035             : 
    2036             :     /* -------------------------------------------------------------------- */
    2037             :     /*      Grow the cache if it isn't large enough to hold the newly       */
    2038             :     /*      requested geometry id.                                          */
    2039             :     /* -------------------------------------------------------------------- */
    2040           0 :     if (nGeomId >= nLineCacheSize)
    2041             :     {
    2042           0 :         const int nNewSize = nGeomId + 100;
    2043             : 
    2044           0 :         papoLineCache = static_cast<OGRGeometry **>(
    2045           0 :             CPLRealloc(papoLineCache, sizeof(void *) * nNewSize));
    2046           0 :         memset(papoLineCache + nLineCacheSize, 0,
    2047           0 :                sizeof(void *) * (nNewSize - nLineCacheSize));
    2048           0 :         nLineCacheSize = nNewSize;
    2049             :     }
    2050             : 
    2051             :     /* -------------------------------------------------------------------- */
    2052             :     /*      Make a cloned copy of the geometry for the cache.               */
    2053             :     /* -------------------------------------------------------------------- */
    2054           0 :     if (papoLineCache[nGeomId] != nullptr)
    2055           0 :         return;
    2056             : 
    2057           0 :     papoLineCache[nGeomId] = poGeometry->clone();
    2058             : }
    2059             : 
    2060             : /************************************************************************/
    2061             : /*                          CacheGetByGeomId()                          */
    2062             : /************************************************************************/
    2063             : 
    2064           0 : OGRGeometry *NTFFileReader::CacheGetByGeomId(int nGeomId)
    2065             : 
    2066             : {
    2067           0 :     if (nGeomId < 0 || nGeomId >= nLineCacheSize)
    2068           0 :         return nullptr;
    2069             :     else
    2070           0 :         return papoLineCache[nGeomId];
    2071             : }
    2072             : 
    2073             : /************************************************************************/
    2074             : /*                             CacheClean()                             */
    2075             : /************************************************************************/
    2076             : 
    2077           0 : void NTFFileReader::CacheClean()
    2078             : 
    2079             : {
    2080           0 :     for (int i = 0; i < nLineCacheSize; i++)
    2081             :     {
    2082           0 :         if (papoLineCache[i] != nullptr)
    2083           0 :             delete papoLineCache[i];
    2084             :     }
    2085           0 :     if (papoLineCache != nullptr)
    2086           0 :         CPLFree(papoLineCache);
    2087             : 
    2088           0 :     nLineCacheSize = 0;
    2089           0 :     papoLineCache = nullptr;
    2090           0 : }
    2091             : 
    2092             : /************************************************************************/
    2093             : /*                      CacheLineGeometryInGroup()                      */
    2094             : /*                                                                      */
    2095             : /*      Run any line geometries in this group through the               */
    2096             : /*      ProcessGeometry() call just to ensure the line geometry will    */
    2097             : /*      be cached.                                                      */
    2098             : /************************************************************************/
    2099             : 
    2100           0 : void NTFFileReader::CacheLineGeometryInGroup(NTFRecord **papoGroup)
    2101             : 
    2102             : {
    2103           0 :     if (!bCacheLines)
    2104           0 :         return;
    2105             : 
    2106           0 :     for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
    2107             :     {
    2108           0 :         if (papoGroup[iRec]->GetType() == NRT_GEOMETRY ||
    2109           0 :             papoGroup[iRec]->GetType() == NRT_GEOMETRY3D)
    2110             :         {
    2111           0 :             OGRGeometry *poGeom = ProcessGeometry(papoGroup[iRec], nullptr);
    2112           0 :             if (poGeom != nullptr)
    2113           0 :                 delete poGeom;
    2114             :         }
    2115             :     }
    2116             : }
    2117             : 
    2118             : /************************************************************************/
    2119             : /*                        FormPolygonFromCache()                        */
    2120             : /*                                                                      */
    2121             : /*      This method will attempt to find the line geometries            */
    2122             : /*      referenced by the GEOM_ID_OF_LINK ids of a feature in the       */
    2123             : /*      line cache (if available), and if so, assemble them into a      */
    2124             : /*      polygon.                                                        */
    2125             : /************************************************************************/
    2126             : 
    2127           0 : int NTFFileReader::FormPolygonFromCache(OGRFeature *poFeature)
    2128             : 
    2129             : {
    2130           0 :     if (!bCacheLines)
    2131           0 :         return FALSE;
    2132             : 
    2133             :     /* -------------------------------------------------------------------- */
    2134             :     /*      Collect all the linked lines.                                   */
    2135             :     /* -------------------------------------------------------------------- */
    2136           0 :     int nLinkCount = 0;
    2137             :     const int *panLinks =
    2138           0 :         poFeature->GetFieldAsIntegerList("GEOM_ID_OF_LINK", &nLinkCount);
    2139             : 
    2140           0 :     if (panLinks == nullptr)
    2141           0 :         return FALSE;
    2142             : 
    2143           0 :     OGRGeometryCollection oLines;
    2144             : 
    2145           0 :     for (int i = 0; i < nLinkCount; i++)
    2146             :     {
    2147           0 :         OGRGeometry *poLine = CacheGetByGeomId(panLinks[i]);
    2148           0 :         if (poLine == nullptr)
    2149             :         {
    2150           0 :             oLines.removeGeometry(-1, FALSE);
    2151           0 :             return FALSE;
    2152             :         }
    2153             : 
    2154           0 :         oLines.addGeometryDirectly(poLine);
    2155             :     }
    2156             : 
    2157             :     /* -------------------------------------------------------------------- */
    2158             :     /*      Assemble into a polygon geometry.                               */
    2159             :     /* -------------------------------------------------------------------- */
    2160           0 :     OGRGeometry *poGeom = OGRGeometry::FromHandle(OGRBuildPolygonFromEdges(
    2161             :         (OGRGeometryH)&oLines, FALSE, FALSE, 0.1, nullptr));
    2162             : 
    2163           0 :     poFeature->SetGeometryDirectly(poGeom);
    2164             : 
    2165           0 :     oLines.removeGeometry(-1, FALSE);
    2166             : 
    2167           0 :     return poGeom != nullptr;
    2168             : }

Generated by: LCOV version 1.14