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

Generated by: LCOV version 1.14