LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/ntf - ogrntfdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 49 192 25.5 %
Date: 2026-03-26 23:25:44 Functions: 4 15 26.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  UK NTF Reader
       4             :  * Purpose:  Implements OGRNTFDataSource class
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ntf.h"
      14             : #include "cpl_conv.h"
      15             : #include "cpl_string.h"
      16             : 
      17             : /************************************************************************/
      18             : /*                          OGRNTFDataSource()                          */
      19             : /************************************************************************/
      20             : 
      21         819 : OGRNTFDataSource::OGRNTFDataSource()
      22             :     : nLayers(0), papoLayers(nullptr), poFCLayer(nullptr), iCurrentFC(0),
      23             :       iCurrentReader(-1), nCurrentPos(0), nCurrentFID(0), nNTFFileCount(0),
      24             :       papoNTFFileReader(nullptr), nFCCount(0), papszFCNum(nullptr),
      25             :       papszFCName(nullptr),
      26             :       poSpatialRef(new OGRSpatialReference(
      27             :           "PROJCS[\"OSGB 1936 / British National Grid\",GEOGCS[\"OSGB 1936\","
      28             :           "DATUM[\"OSGB_1936\",SPHEROID[\"Airy 1830\",6377563.396,299.3249646,"
      29             :           "AUTHORITY[\"EPSG\",\"7001\"]],AUTHORITY[\"EPSG\",\"6277\"]],"
      30             :           "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
      31             :           "UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4277\"]],"
      32             :           "PROJECTION[\"Transverse_Mercator\"],"
      33             :           "PARAMETER[\"latitude_of_origin\",49],"
      34             :           "PARAMETER[\"central_meridian\",-2],"
      35             :           "PARAMETER[\"scale_factor\",0.999601272],"
      36             :           "PARAMETER[\"false_easting\",400000],"
      37             :           "PARAMETER[\"false_northing\",-100000],"
      38             :           "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],"
      39         819 :           "AUTHORITY[\"EPSG\",\"27700\"]]")),
      40       83538 :       papszOptions(nullptr)
      41             : {
      42         819 :     poSpatialRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      43             : 
      44             :     /* -------------------------------------------------------------------- */
      45             :     /*      Allow initialization of options from the environment.           */
      46             :     /* -------------------------------------------------------------------- */
      47         819 :     if (getenv("OGR_NTF_OPTIONS") != nullptr)
      48             :     {
      49           0 :         papszOptions = CSLTokenizeStringComplex(getenv("OGR_NTF_OPTIONS"), ",",
      50             :                                                 FALSE, FALSE);
      51             :     }
      52         819 : }
      53             : 
      54             : /************************************************************************/
      55             : /*                         ~OGRNTFDataSource()                          */
      56             : /************************************************************************/
      57             : 
      58       83538 : OGRNTFDataSource::~OGRNTFDataSource()
      59             : 
      60             : {
      61         819 :     for (int i = 0; i < nNTFFileCount; i++)
      62           0 :         delete papoNTFFileReader[i];
      63             : 
      64         819 :     CPLFree(papoNTFFileReader);
      65             : 
      66         819 :     for (int i = 0; i < nLayers; i++)
      67           0 :         delete papoLayers[i];
      68             : 
      69         819 :     if (poFCLayer != nullptr)
      70           0 :         delete poFCLayer;
      71             : 
      72         819 :     CPLFree(papoLayers);
      73             : 
      74         819 :     CSLDestroy(papszOptions);
      75             : 
      76         819 :     CSLDestroy(papszFCNum);
      77         819 :     CSLDestroy(papszFCName);
      78             : 
      79         819 :     if (poSpatialRef)
      80         819 :         poSpatialRef->Release();
      81        1638 : }
      82             : 
      83             : /************************************************************************/
      84             : /*                           TestCapability()                           */
      85             : /************************************************************************/
      86             : 
      87           0 : int OGRNTFDataSource::TestCapability(const char *pszCap) const
      88             : 
      89             : {
      90           0 :     if (EQUAL(pszCap, ODsCZGeometries))
      91           0 :         return true;
      92             : 
      93           0 :     return false;
      94             : }
      95             : 
      96             : /************************************************************************/
      97             : /*                           GetNamedLayer()                            */
      98             : /************************************************************************/
      99             : 
     100           0 : OGRNTFLayer *OGRNTFDataSource::GetNamedLayer(const char *pszNameIn)
     101             : 
     102             : {
     103           0 :     for (int i = 0; i < nLayers; i++)
     104             :     {
     105           0 :         if (EQUAL(papoLayers[i]->GetLayerDefn()->GetName(), pszNameIn))
     106           0 :             return static_cast<OGRNTFLayer *>(papoLayers[i]);
     107             :     }
     108             : 
     109           0 :     return nullptr;
     110             : }
     111             : 
     112             : /************************************************************************/
     113             : /*                              AddLayer()                              */
     114             : /************************************************************************/
     115             : 
     116           0 : void OGRNTFDataSource::AddLayer(OGRLayer *poNewLayer)
     117             : 
     118             : {
     119           0 :     papoLayers = static_cast<OGRLayer **>(
     120           0 :         CPLRealloc(papoLayers, sizeof(void *) * ++nLayers));
     121             : 
     122           0 :     papoLayers[nLayers - 1] = poNewLayer;
     123           0 : }
     124             : 
     125             : /************************************************************************/
     126             : /*                              GetLayer()                              */
     127             : /************************************************************************/
     128             : 
     129           0 : OGRLayer *OGRNTFDataSource::GetLayer(int iLayer) const
     130             : 
     131             : {
     132           0 :     if (iLayer < 0 || iLayer > nLayers)
     133           0 :         return nullptr;
     134           0 :     else if (iLayer == nLayers)
     135           0 :         return poFCLayer;
     136             :     else
     137           0 :         return papoLayers[iLayer];
     138             : }
     139             : 
     140             : /************************************************************************/
     141             : /*                           GetLayerCount()                            */
     142             : /************************************************************************/
     143             : 
     144           0 : int OGRNTFDataSource::GetLayerCount() const
     145             : 
     146             : {
     147           0 :     if (poFCLayer == nullptr)
     148           0 :         return nLayers;
     149             :     else
     150           0 :         return nLayers + 1;
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /*                                Open()                                */
     155             : /************************************************************************/
     156             : 
     157         819 : int OGRNTFDataSource::Open(const char *pszFilename, int bTestOpen,
     158             :                            char **papszLimitedFileList)
     159             : 
     160             : {
     161             :     VSIStatBufL stat;
     162         819 :     char **papszFileList = nullptr;
     163             : 
     164             :     /* -------------------------------------------------------------------- */
     165             :     /*      Is the given path a directory or a regular file?                */
     166             :     /* -------------------------------------------------------------------- */
     167        1638 :     if (VSIStatL(pszFilename, &stat) != 0 ||
     168         819 :         (!VSI_ISDIR(stat.st_mode) && !VSI_ISREG(stat.st_mode)))
     169             :     {
     170           0 :         if (!bTestOpen)
     171           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     172             :                      "%s is neither a file or directory, NTF access failed.\n",
     173             :                      pszFilename);
     174             : 
     175           0 :         return FALSE;
     176             :     }
     177             : 
     178             :     /* -------------------------------------------------------------------- */
     179             :     /*      Build a list of filenames we figure are NTF files.              */
     180             :     /* -------------------------------------------------------------------- */
     181         819 :     if (VSI_ISREG(stat.st_mode))
     182             :     {
     183          60 :         papszFileList = CSLAddString(nullptr, pszFilename);
     184             :     }
     185             :     else
     186             :     {
     187         759 :         char **candidateFileList = VSIReadDir(pszFilename);
     188             : 
     189       26841 :         for (int i = 0;
     190       26841 :              candidateFileList != nullptr && candidateFileList[i] != nullptr;
     191             :              i++)
     192             :         {
     193       26082 :             if (papszLimitedFileList != nullptr &&
     194           0 :                 CSLFindString(papszLimitedFileList, candidateFileList[i]) == -1)
     195             :             {
     196           0 :                 continue;
     197             :             }
     198             : 
     199       26082 :             if (strlen(candidateFileList[i]) > 4 &&
     200       24770 :                 STARTS_WITH_CI(candidateFileList[i] +
     201             :                                    strlen(candidateFileList[i]) - 4,
     202             :                                ".ntf"))
     203             :             {
     204             :                 papszFileList =
     205           0 :                     CSLAddString(papszFileList,
     206           0 :                                  CPLFormFilenameSafe(
     207           0 :                                      pszFilename, candidateFileList[i], nullptr)
     208             :                                      .c_str());
     209             :             }
     210             :         }
     211             : 
     212         759 :         CSLDestroy(candidateFileList);
     213             : 
     214         759 :         if (CSLCount(papszFileList) == 0)
     215             :         {
     216         759 :             if (!bTestOpen)
     217           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
     218             :                          "No candidate NTF files (.ntf) found in\n"
     219             :                          "directory: %s",
     220             :                          pszFilename);
     221         759 :             CSLDestroy(papszFileList);
     222         759 :             return FALSE;
     223             :         }
     224             :     }
     225             : 
     226             :     /* -------------------------------------------------------------------- */
     227             :     /*      Loop over all these files trying to open them.  In testopen     */
     228             :     /*      mode we first read the first 80 characters, to verify that      */
     229             :     /*      it looks like an NTF file.  Note that we don't keep the file    */
     230             :     /*      open ... we don't want to occupy a lot of file handles when      */
     231             :     /*      handling a whole directory.                                     */
     232             :     /* -------------------------------------------------------------------- */
     233          60 :     papoNTFFileReader = static_cast<NTFFileReader **>(
     234          60 :         CPLCalloc(sizeof(void *), CSLCount(papszFileList)));
     235             : 
     236         120 :     for (int i = 0; papszFileList != nullptr && papszFileList[i] != nullptr;
     237             :          i++)
     238             :     {
     239          60 :         if (bTestOpen)
     240             :         {
     241          60 :             VSILFILE *fp = VSIFOpenL(papszFileList[i], "rb");
     242          60 :             if (fp == nullptr)
     243          60 :                 continue;
     244             : 
     245          57 :             char szHeader[80] = {};
     246          57 :             if (VSIFReadL(szHeader, 80, 1, fp) < 1)
     247             :             {
     248          57 :                 VSIFCloseL(fp);
     249          57 :                 continue;
     250             :             }
     251             : 
     252           0 :             VSIFCloseL(fp);
     253             : 
     254           0 :             if (!STARTS_WITH_CI(szHeader, "01"))
     255           0 :                 continue;
     256             : 
     257           0 :             int j = 0;  // Used after for.
     258           0 :             for (; j < 80; j++)
     259             :             {
     260           0 :                 if (szHeader[j] == 10 || szHeader[j] == 13)
     261             :                     break;
     262             :             }
     263             : 
     264           0 :             if (j == 80 || (j > 0 && szHeader[j - 1] != '%'))
     265           0 :                 continue;
     266             :         }
     267             : 
     268           0 :         NTFFileReader *poFR = new NTFFileReader(this);
     269             : 
     270           0 :         if (!poFR->Open(papszFileList[i]))
     271             :         {
     272           0 :             delete poFR;
     273           0 :             CSLDestroy(papszFileList);
     274             : 
     275           0 :             return FALSE;
     276             :         }
     277             : 
     278           0 :         poFR->SetBaseFID(nNTFFileCount * 1000000 + 1);
     279           0 :         poFR->Close();
     280             : 
     281           0 :         EnsureTileNameUnique(poFR);
     282             : 
     283           0 :         papoNTFFileReader[nNTFFileCount++] = poFR;
     284             :     }
     285             : 
     286          60 :     CSLDestroy(papszFileList);
     287             : 
     288          60 :     if (nNTFFileCount == 0)
     289          60 :         return FALSE;
     290             : 
     291             :     /* -------------------------------------------------------------------- */
     292             :     /*      Establish generic layers.                                       */
     293             :     /* -------------------------------------------------------------------- */
     294           0 :     EstablishGenericLayers();
     295             : 
     296             :     /* -------------------------------------------------------------------- */
     297             :     /*      Loop over all the files, collecting a unique feature class      */
     298             :     /*      listing.                                                        */
     299             :     /* -------------------------------------------------------------------- */
     300           0 :     for (int iSrcFile = 0; iSrcFile < nNTFFileCount; iSrcFile++)
     301             :     {
     302           0 :         NTFFileReader *poSrcReader = papoNTFFileReader[iSrcFile];
     303             : 
     304           0 :         for (int iSrcFC = 0; iSrcFC < poSrcReader->GetFCCount(); iSrcFC++)
     305             :         {
     306           0 :             char *pszSrcFCName = nullptr;
     307           0 :             char *pszSrcFCNum = nullptr;
     308             : 
     309           0 :             poSrcReader->GetFeatureClass(iSrcFC, &pszSrcFCNum, &pszSrcFCName);
     310             : 
     311           0 :             int iDstFC = 0;
     312           0 :             for (; iDstFC < nFCCount; iDstFC++)
     313             :             {
     314           0 :                 if (EQUAL(pszSrcFCNum, papszFCNum[iDstFC]))
     315           0 :                     break;
     316             :             }
     317             : 
     318           0 :             if (iDstFC >= nFCCount)
     319             :             {
     320           0 :                 nFCCount++;
     321           0 :                 papszFCNum = CSLAddString(papszFCNum, pszSrcFCNum);
     322           0 :                 papszFCName = CSLAddString(papszFCName, pszSrcFCName);
     323             :             }
     324             :         }
     325             :     }
     326             : 
     327             :     /* -------------------------------------------------------------------- */
     328             :     /*      Create a new layer specifically for feature classes.            */
     329             :     /* -------------------------------------------------------------------- */
     330           0 :     if (nFCCount > 0)
     331           0 :         poFCLayer = new OGRNTFFeatureClassLayer(this);
     332             :     else
     333           0 :         poFCLayer = nullptr;
     334             : 
     335           0 :     return TRUE;
     336             : }
     337             : 
     338             : /************************************************************************/
     339             : /*                            ResetReading()                            */
     340             : /*                                                                      */
     341             : /*      Cleanup, and start over.                                        */
     342             : /************************************************************************/
     343             : 
     344           0 : void OGRNTFDataSource::ResetReading()
     345             : 
     346             : {
     347           0 :     for (int i = 0; i < nNTFFileCount; i++)
     348           0 :         papoNTFFileReader[i]->Close();
     349             : 
     350           0 :     iCurrentReader = -1;
     351           0 :     nCurrentPos = (vsi_l_offset)-1;
     352           0 :     nCurrentFID = 1;
     353           0 :     iCurrentFC = 0;
     354           0 : }
     355             : 
     356             : /************************************************************************/
     357             : /*                           GetNextFeature()                           */
     358             : /************************************************************************/
     359             : 
     360           0 : OGRFeature *OGRNTFDataSource::GetNextFeature(OGRLayer **ppoBelongingLayer,
     361             :                                              double *pdfProgressPct,
     362             :                                              GDALProgressFunc /* pfnProgress */,
     363             :                                              void * /* pProgressData */)
     364             : 
     365             : {
     366           0 :     if (pdfProgressPct != nullptr)
     367           0 :         *pdfProgressPct = 0.0;
     368           0 :     if (ppoBelongingLayer != nullptr)
     369           0 :         *ppoBelongingLayer = nullptr;
     370             : 
     371           0 :     OGRFeature *poFeature = nullptr;
     372             : 
     373             :     /* -------------------------------------------------------------------- */
     374             :     /*      If we have already read all the conventional features, we       */
     375             :     /*      should try and return feature class features.                   */
     376             :     /* -------------------------------------------------------------------- */
     377           0 :     if (iCurrentReader == nNTFFileCount)
     378             :     {
     379           0 :         if (iCurrentFC < nFCCount)
     380           0 :             return poFCLayer->GetFeature(iCurrentFC++);
     381             :         else
     382           0 :             return nullptr;
     383             :     }
     384             : 
     385             :     /* -------------------------------------------------------------------- */
     386             :     /*      Do we need to open a file?                                      */
     387             :     /* -------------------------------------------------------------------- */
     388           0 :     if (iCurrentReader == -1)
     389             :     {
     390           0 :         iCurrentReader++;
     391           0 :         nCurrentPos = (vsi_l_offset)-1;
     392             :     }
     393             : 
     394           0 :     if (papoNTFFileReader[iCurrentReader]->GetFP() == nullptr)
     395             :     {
     396           0 :         papoNTFFileReader[iCurrentReader]->Open();
     397             :     }
     398             : 
     399             :     /* -------------------------------------------------------------------- */
     400             :     /*      Ensure we are reading on from the same point we were reading    */
     401             :     /*      from for the last feature, even if some other access            */
     402             :     /*      mechanism has moved the file pointer.                           */
     403             :     /* -------------------------------------------------------------------- */
     404           0 :     if (nCurrentPos != (vsi_l_offset)-1)
     405           0 :         papoNTFFileReader[iCurrentReader]->SetFPPos(nCurrentPos, nCurrentFID);
     406             : 
     407             :     /* -------------------------------------------------------------------- */
     408             :     /*      Read a feature.  If we get NULL the file must be all            */
     409             :     /*      consumed, advance to the next file.                             */
     410             :     /* -------------------------------------------------------------------- */
     411           0 :     poFeature = papoNTFFileReader[iCurrentReader]->ReadOGRFeature();
     412           0 :     if (poFeature == nullptr)
     413             :     {
     414           0 :         papoNTFFileReader[iCurrentReader]->Close();
     415           0 :         if (GetOption("CACHING") != nullptr &&
     416           0 :             EQUAL(GetOption("CACHING"), "OFF"))
     417           0 :             papoNTFFileReader[iCurrentReader]->DestroyIndex();
     418             : 
     419           0 :         iCurrentReader++;
     420           0 :         nCurrentPos = (vsi_l_offset)-1;
     421           0 :         nCurrentFID = 1;
     422             : 
     423           0 :         poFeature = GetNextFeature(nullptr, nullptr, nullptr, nullptr);
     424             :     }
     425             :     else
     426             :     {
     427           0 :         papoNTFFileReader[iCurrentReader]->GetFPPos(&nCurrentPos, &nCurrentFID);
     428             :     }
     429             : 
     430           0 :     return poFeature;
     431             : }
     432             : 
     433             : /************************************************************************/
     434             : /*                          GetFeatureClass()                           */
     435             : /************************************************************************/
     436             : 
     437           0 : int OGRNTFDataSource::GetFeatureClass(int iFCIndex, char **ppszFCId,
     438             :                                       char **ppszFCName)
     439             : 
     440             : {
     441           0 :     if (iFCIndex < 0 || iFCIndex >= nFCCount)
     442             :     {
     443           0 :         *ppszFCId = nullptr;
     444           0 :         *ppszFCName = nullptr;
     445           0 :         return FALSE;
     446             :     }
     447             :     else
     448             :     {
     449           0 :         *ppszFCId = papszFCNum[iFCIndex];
     450           0 :         *ppszFCName = papszFCName[iFCIndex];
     451           0 :         return TRUE;
     452             :     }
     453             : }
     454             : 
     455             : /************************************************************************/
     456             : /*                             SetOptions()                             */
     457             : /************************************************************************/
     458             : 
     459           0 : void OGRNTFDataSource::SetOptionList(char **papszNewOptions)
     460             : 
     461             : {
     462           0 :     CSLDestroy(papszOptions);
     463           0 :     papszOptions = CSLDuplicate(papszNewOptions);
     464           0 : }
     465             : 
     466             : /************************************************************************/
     467             : /*                             GetOption()                              */
     468             : /************************************************************************/
     469             : 
     470           0 : const char *OGRNTFDataSource::GetOption(const char *pszOption)
     471             : 
     472             : {
     473           0 :     return CSLFetchNameValue(papszOptions, pszOption);
     474             : }
     475             : 
     476             : /************************************************************************/
     477             : /*                        EnsureTileNameUnique()                        */
     478             : /*                                                                      */
     479             : /*      This method is called with an NTFFileReader to ensure that      */
     480             : /*      its tilename is unique relative to all the readers already      */
     481             : /*      assigned to this data source.  If not, a unique name is         */
     482             : /*      selected for it and assigned.  This method should not be        */
     483             : /*      called with readers that are already attached to the data      */
     484             : /*      source.                                                         */
     485             : /************************************************************************/
     486             : 
     487           0 : void OGRNTFDataSource::EnsureTileNameUnique(NTFFileReader *poNewReader)
     488             : 
     489             : {
     490           0 :     int iSequenceNumber = -1;
     491           0 :     bool bIsUnique = false;
     492           0 :     char szCandidateName[12] = {};
     493             : 
     494           0 :     do
     495             :     {
     496           0 :         bIsUnique = TRUE;
     497           0 :         if (iSequenceNumber++ == -1)
     498           0 :             strncpy(szCandidateName, poNewReader->GetTileName(),
     499             :                     sizeof(szCandidateName) - 1);
     500             :         else
     501           0 :             snprintf(szCandidateName, sizeof(szCandidateName), "%010d",
     502             :                      iSequenceNumber);
     503             : 
     504           0 :         for (int iReader = 0; iReader < nNTFFileCount && bIsUnique; iReader++)
     505             :         {
     506           0 :             const char *pszTileName = GetFileReader(iReader)->GetTileName();
     507           0 :             if (pszTileName != nullptr &&
     508           0 :                 strcmp(szCandidateName, pszTileName) == 0)
     509             :             {
     510           0 :                 bIsUnique = FALSE;
     511             :             }
     512             :         }
     513           0 :     } while (!bIsUnique);
     514             : 
     515           0 :     if (iSequenceNumber > 0)
     516             :     {
     517           0 :         poNewReader->OverrideTileName(szCandidateName);
     518           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     519             :                  "Forcing TILE_REF to `%s' on file %s\n"
     520             :                  "to avoid conflict with other tiles in this data source.",
     521             :                  szCandidateName, poNewReader->GetFilename());
     522             :     }
     523           0 : }

Generated by: LCOV version 1.14